主讲人:徐静 @ 联信Web全栈数据工程师培训
2017年6月14日
NLP=Natural Language Processing
机器学习的一个分支。目标是使机器学习,识别,理解人类使用的自然语言(语音,字符文字,图像), 具备使用自然语言与人类进行交流的能力
目前机器学习领域最困难的技术之一,里面的难点大部分成为各个应用领域(搜索引擎,情感识别,机器写作等等)的核心障碍,是实现高度智能机器人的核心技术
NLP大部分方法适用于不同的语种,也有部分只适合特定语种
文本挖掘可视为NLP的一个子领域,目标是在大量非结构化或结构化文本中整理吸取有价值的内容,文本自动分类,判同,情感分析是比较常见的应用
NLG=Natural Language Generation
世界上比较NB的产品
我们的工作会很艰难!!!
最大匹配法: 设定最大词长,从左到右匹配 最大匹配法,双向匹配法,最佳匹配法,联想回溯法 (eg:“长春市长春节致辞”)
最大概率法: 一个待切分的汉字串可能包含多种分词结果,将其中概率 最大的那个作为该字符串的分词结果 条件概率近似公式 (eg: “乒乓球拍卖完了”)
最短路径分词方法: 在词图上选择一条词数量最少的路径 (“他说的确实在理”)
隐马尔可夫模型: HMM基于马尔科夫过程利用转移概率分词
混合模型: 它结合使用最大概率法和隐式马尔科夫模型
索引模型:先使用混合模型进行切词,再对于切出来的较长的词,枚举句子中所有可能成词的情况,找出词库里存在的词
布尔模型:以集合论和布尔代数为基础,进行布尔逻辑运算
向量空间模型:基于概率论和信息论,将文档转化为向量,看做空间中的一个点
文档概率模型:基于Baysian统计
NLP应用首先是对文本进行分词,当前中文分词器有Ansj、paoding、盘古分词等多种, # 而最基础的分词器应该属于jieba分词器
jiebaR提供了可以通过函数worker()来初始化分词引擎,使用函数segment()进行分词。其提供了四种分词模式,分别基于以下算法(摘自官方文档): 最大概率法(MPSegment),负责根据Trie树构建有向无环图和进行动态规划算法,是分词算法的核心。 隐式马尔科夫模型(HMMSegment)是根据基于人民日报等语料库构建的HMM模型来进行分词,主要算法思路是根据(B,E,M,S)四个状态来代表每个字的隐藏状态。 HMM模型由dict/hmm_model.utf8提供。分词算法即viterbi算法。 混合模型(MixSegment)是四个分词引擎里面分词效果较好的类,结它合使用最大概率法和隐式马尔科夫模型。 索引模型(QuerySegment)先使用混合模型进行切词,再对于切出来的较长的词,枚举句子中所有可能成词的情况,找出词库里存在。
i.中文分词
library(jiebaRD)
library(jiebaR)
# worker(type = "mix", dict = DICTPATH, hmm = HMMPATH, user = USERPATH,
# idf = IDFPATH, stop_word = STOPPATH, write = T, qmax = 20, topn = 5,
# encoding = "UTF-8", detect = T, symbol = F, lines = 1e+05,
# output = NULL, bylines = F, user_weight = "max")
#
# 参数列表:
#
# type, 引擎类型
# dict, 系统词典
# hmm, HMM模型路径
# user, 用户词典
# idf, IDF词典
# stop_word, 关键词用停止词库
# write, 是否将文件分词结果写入文件,默认FALSE
# qmax, 最大成词的字符数,默认20个字符
# topn, 关键词数,默认5个
# encoding, 输入文件的编码,默认UTF-8
# detect, 是否编码检查,默认TRUE
# symbol, 是否保留符号,默认FALSE
# lines, 每次读取文件的最大行数,用于控制读取文件的长度。大文件则会分次读取。
# output, 输出路径
# bylines, 按行输出
# user_weight, 用户权重
#查看系统词典
# scan(file="D:/tool/R-3.2.3/library/jiebaRD/dict/jieba.dict.utf8",
# what=character(),nlines=50,sep='\n',
# encoding='utf-8',fileEncoding='utf-8')
# 通过函数worker()来初始化分词引擎,使用segment()进行分词。有四种分词模式:最大概率法(MP)、隐马尔科夫模型(HMM)、
# 混合模型(Mix)及索引模型(query),默认为混合模型。具体可查看help(worker).
#help(worker)
mixseg<-worker()
# worker()函数是jiebaR里最为核心的函数,我们可以设置其众多参数:
# cc2 = worker(type = "mix", dict = "dict/jieba.dict.utf8",
# hmm = "dict/hmm_model.utf8",
# user = "dict/test.dict.utf8",
# symbol = F
# )
# 这段代码展示了其中比较关键的一些参数,其中type是算法类型,dict是字典来源,如果不设置就会使用jiebaR默认的字典,
# 同样的hmm路径和user路径也一样。symbol选项是F表示不保留标点。此外,还可以设置停词字典,更多详细具体的设置可以在R语言里用
segment(c("这是一段测试文本","徐静是下一届美国总统"),mixseg)## [1] "这是" "一段" "测试" "文本" "徐静" "是" "下" "一届" "美国" "总统"
mixseg["这是一段测试文本"]## [1] "这是" "一段" "测试" "文本"
mixseg<="这是语段测试文本"## [1] "这是" "语段" "测试" "文本"
ii.词性标注
# 可以使用<=.tagger 或者tag 来进行分词和词性标注,
# 词性标注使用混合模型模型分词,标注采用和 ictclas 兼容的标记法。
words = "我爱青岛,我爱联信集团"
tagger = worker("tag") #开启词性标注启发器
tagger <= words## r v ns r v v n
## "我" "爱" "青岛" "我" "爱" "联信" "集团"
iii.关键字提取
# R关键词提取使用逆向文件频率(IDF)文本语料库,
# 通过worker参数“keywords”开启关键词提取启发器,topn参数为关键词的个数。
keys = worker("keywords",topn = 5, idf = IDFPATH)
keys <= "我爱北京天安门"## 8.9954 4.6674
## "天安门" "北京"
#返回结果之所以是天安门而没有“北京”和“”我”等词是因为这些词的相对词频不高,在行文中太过常见。topn表示的是返回关键词的个数,8.9954就是相对词频值。iiii.simhash计算
# 对中文文档计算出对应的simhash值。simhash是谷歌用来进行文本去重的算法,
#现在广泛应用在文本处理中。Simhash引擎先进行分词和关键词提取,后计算Simhash值和海明距离,官方例子如下:
mixseg<="江州市长江大桥参加了长江大桥的通车仪式"## [1] "江州" "市长" "江大桥" "参加" "了" "长江大桥"
## [7] "的" "通车" "仪式"
words = "hello world!"
simhasher = worker("simhash",topn=2)
simhasher <= "江州市长江大桥参加了长江大桥的通车仪式"## $simhash
## [1] "12882166450308878002"
##
## $keyword
## 22.3853 8.69667
## "长江大桥" "江州"
distance(words, "江州市长江大桥参加了长江大桥的通车仪式",simhasher)## $distance
## [1] 23
##
## $lhs
## 11.7392 11.7392
## "hello" "world"
##
## $rhs
## 22.3853 8.69667
## "长江大桥" "江州"
iiiii.分词词典添加
#法1:添加用户词库
# engine_new_word<-worker()
# new_user_word(engine_new_word, c("公众号","R语言"))
# segment(words,engine_new_word)
#法2:
# 可以自定义用户词库,推荐使用深蓝词库转换构建分词词库,
# 它可以快速地将搜狗细胞词库等输入法词库转换为jiebaR的词库格式。
#
# ShowDictPath() ### 显示词典路径
# EditDict() ### 编辑用户词典
# ?EditDict() ### 打开帮助系统
#注意
# (1)词库的第一行一定要空着,否则第一个词就会莫名其妙的失效。
#
# (2)如果你的词库是用记事本写的话,因为编码有时不是UTF-8,使用时会出现 各种错误,甚至软件奔溃。所以建议使用Emeditor或notepad++编辑,将编码设置为utf-8,另存为txt文件。
#
# (3)如果你需要添加搜狗细胞词库的话,那你需要安装cidian包,它可以帮助 我们把搜狗细胞词库转换为jiebaR可以使用的词库。
# 法3:分词词典工具
#
# Linux & Mac: Build Status Win : Build status
#
# 安装
#
# install.packages("devtools")
# install.packages("stringi")
# install.packages("pbapply")
# install.packages("Rcpp")
# install.packages("RcppProgress")
# library(devtools)
# install_github("qinwf/cidian")
# 使用
#
# decode_scel(scel = "细胞词库路径", output = "输出文件路径", cpp = TRUE)
#
# decode_scel(scel = "细胞词库路径",output = "输出文件路径",cpp = FALSE, progress = TRUE)
#
# # 输出调试信息
# decode_scel(scel = "细胞词库路径", output = "输出文件路径", cpp = FALSE, progress = TRUE, rdebug = TRUE)
#
# # system dict with frequency
# decode_scel("细胞词库路径", output = "输出文件路径", sysdict_freq = 1)
# 读取词典和编辑词典文件
#
# ## 读取用户词典
#
# load_user_dict(filePath = "用户词典路径", default_tag = "默认标记")
#
# ## 读取系统词典
# load_sys_dict(filePath = "系统词典路径")
#
# ## 增加用户词典词
#
# add_user_words(dict = "load_user_dict 读取的词典", words = "UTF-8 编码文本向量", tags = "标记")
#
# ## 增加系统词典词
#
# add_sys_words(dict = "load_sys_dict 读取的词典", words = "UTF-8 编码文本向量", freq = "词频", tags = "标记")
#
# ## 删除词典词
#
# remove_words(dict = "load_user_dict 或 load_sys_dict 读取的词典", words = "UTF-8 编码文本向量")
#
# ## 写入
#
# write_dict(dict = "load_user_dict 或 load_sys_dict 读取的词典", output = "输出路径")
# (userd = load_user_dict(jiebaR::USERPATH))
#
# userd = add_user_words(userd, enc2utf8("测试"), "v")
#
# write_dict(userd, jiebaR::USERPATH)
#
# (userd = load_user_dict(jiebaR::USERPATH))
# userd = remove_words(userd, enc2utf8(c("测试","蓝翔")))
#
# write_dict(userd, jiebaR::USERPATH)
#
# (userd = load_user_dict(jiebaR::USERPATH))iiiiii.jiebaR with shiny
iiiiiii.新版jiebaR(2017年4月份更新)
说明文档
整体来说jiebaR还是能满足我们的需求的,此外jieba分词也有Python版本。
# #可以自定义词库,但是是好几代之前的产品,现在用的很少,这个包不好装(r-forge上)
# teststring1 <- "小都督促织女将R语言学习到底"
# teststring2<-"小都是统计之都的昵称:)"
# install.packages("rmmseg4j", repos="http://R-Forge.R-project.org",type='source')
# require(rmmseg4j)
# mmseg4j(teststring1)
# mmseg4j(teststring2)
#
# library(rJava)
#
# #make sure the jar file is in the "java" subdirectory of the “rJava”package.
# # .jpackage(name="rJava",jars="mmseg4j-all-1.8.5-with-dic.jar")
# #
# # c <- .jnew("com/chenlb/mmseg4j/example/Complex");
# # outRef<-.jcall(c, "S", "segWords","我们大家都来学习"," | ",evalString = FALSE)
# # .jstrVal(outRef)imdict-chinese-analyzer 是imdict智能词典的智能中文分词模块,作者高小平,是基于隐马尔可夫(HMM)模型做的 是中国科学院计算机研究所的ictclas中文分词程序的重现(Java),可以直接为lucene 搜索引擎提供中文分词支持。
ictclas应该是目前分词准确度和效率上最好的工具 只可惜是商业软件,imdict-chinese-analyzer是‘ 它的一个开源Java版本,但是不能支持词性识别和自定义词库
Rwordseg作者(lijian)主页 上有一个集成了imdict-chinese-analyzer的RsegWord包 另外rmmseg4j的作者还提供了一个rsmartcn,基于smartcn 这是imdict集成到lucene之后的结果,效果和imdict-chinese-analyzer一样。
# require(RsegWord)
# segWord(teststring1)Rwordseg是一个R环境下的中文分词工具,使用rJava 调用Java分词工具Ansj.
Ansj是一个开源的java分词工具,基于中科院的ictclas 中文分词算法,采用隐马尔可夫模型,作者孙健重写了 一个Java版本,并且全部开源,是的Ansj可用于人名识别, 地名识别,组织机构名识别,多级词性标注,关键词提取, 指纹提取,并支持行业词典,用户自定义词典。 项目的Github地址
library(rJava)
library(Rwordseg)## # Version: 0.2-1
#------------------默认分词------------------------------------
#GBK或UTF-8都可,但是保证在R中正常显示
segmentCN("(结合成分子时")## [1] "结合" "成" "分子" "时"
segmentCN(c("说的确实在理","一次性交多少钱")) #输入字符向量,返回列表## [[1]]
## [1] "说" "的" "确实" "在" "理"
##
## [[2]]
## [1] "一次性" "交" "多少" "钱"
###参数nosymbol表示是否输出汉字,英文和数字,默认T
segmentCN("我的数据科学成绩是100分,Good!", nosymbol = FALSE)## [1] "我" "的" "数据" "科学" "成绩" "是" "100分" ","
## [9] "Good" "!"
#---------------词性识别-----------------------------------------
#参数nature可以设置是否输出词性,默认不输出,如果输出,那么返回的向量名为词性的标识
segmentCN("花一元钱买了一朵美丽的花", nature = TRUE)## v m n v ul m a uj v
## "花" "一元" "钱" "买" "了" "一朵" "美丽" "的" "花"
#不过目前的词性识别并没有做到真正意义上的智能词性识别,同一个词
#在语义上的词性还没有办法识别出来。结果还是仅供参考。。。。词性参照表
#---------------------------人名识别---------------------------------------
# isNameRecognition选项可以设置是否进行智能的人名识别,智能识别有时候会和自定义的词库冲突
# 因此默认的选项是不进行人名识别
getOption("isNameRecognition")## [1] FALSE
segmentCN("徐静是昆仑派的新任盟主")## [1] "徐" "静" "是" "昆仑" "派" "的" "新任" "盟主"
segment.options(isNameRecognition = TRUE)
segmentCN("徐静是昆仑派的新任盟主")## [1] "徐静" "是" "昆仑" "派" "的" "新任" "盟主"
getOption("isNameRecognition")## [1] TRUE
segment.options(isNameRecognition = F)#----------------------------tm格式的支持----------------------------------------
#segmentCN函数默认是输出向量和列表,并使用向量的name属性来表示词性,这是R中最常用的数据结构
#但是由于tm包已经成为R中事实的文本挖掘的标准,因此常常会需要使用tm中使用空格分割的单字符串格式
#------returnType参数如果设置成“tm”,则表示输出tm格式的字符串,这时无法输出词性------
#------isfast参数可以设置成直接调用Java包进行最基础的分词,速度快,输出tm格式,无法输出繁体字,也不进行词性识别,仅供参考
#---------------------------对文件的分词---------------------------------------------
#输入参数strwords除了可以是需要分词的字符向量之外,也可以是某个文本文件的路径,可以自动判断自动识别字符编码,全部转化成UTF-8进行处理和输出
#可以使用outfile参数指定输出文件的名称和路径,blocklines表示每次读入的行数,默认是1000行,可以根据自己电脑性能自行设置
#segmentCN("Lianxincuiji.txt")#----------------------------------词典管理-----------------------------------------------
# 该包支持安装新的词典,一次安装之后,每次重启R包都会自动加载,目前支持普通格式的文本词典和搜狗的Secl格式细胞词典
# 函数installDict用来安装新的词典,参数dictpath表示词典的路径,参数dictname表示自定义的词典名称(英文);参数dicttype表示词典的类型,
#目前只支持text,Scel参数load表示安装后是否自动加载到内存,默认T
# 可以安装ansj项目中的自定义词库https://github.com/ansjsun/ansj_seg/blob/master/library/default.dic
# 并不建议
#
# 安装前
listDict()## [1] Name Type Des Path
## <0 rows> (or 0-length row.names)
#uninstallDict()#------------------------------------------自定义文本词典----------------------------
# 如果仅仅是永辉自己添加词汇,没有必要做一个词典进行安装,可以使用自定义词典的功能默认的词典目录为:%R_HOME%\library\Rwordseg\dict,
#可以在其中添加任意后缀为.dic的文本里面可以输入自定义的词,每一行一个词,回车换行。可以直接写在example.dic这个文件,或者参考该文件新建一个dic文件
#
# 每次重启生效,即可生效可运行loadDict()
#手动添加和删除词汇
#在内存中临时添加删除
# insertWords()
# selectWords()
##需要把添加的词汇记录下来设置
# insertWords(words,save=T)
# selectWords(words,save=T)
#--------------------------从Rwordseg0.1-1开始,提供台湾繁体字的支持。-------------------------Rwordseg例子展示:
#------------------------Rwordseg Example----------------------------
library(Rwordseg)
segmentCN(c("如果你因为错过太阳而流泪", "你也会错过星星")) ## [[1]]
## [1] "如果" "你" "因为" "错" "过" "太阳" "而" "流泪"
##
## [[2]]
## [1] "你" "也" "会" "错" "过" "星星"
#发现,“错过”这个词分错了
#加词删词
insertWords("错过")
segmentCN(c("如果你因为错过太阳而流泪", "你也会错过星星")) ## [[1]]
## [1] "如果" "你" "因为" "错过" "太阳" "而" "流泪"
##
## [[2]]
## [1] "你" "也" "会" "错过" "星星"
# 有些情况下,你不希望某个词被分出来,
# 例如还是“错过”这个词,这里“错”和“过”语义上已经不应该是一个词语了,
# 所以,可以从词典中删除这个词,再添加上你需要的词语,继续做分词,效果就好多了。
segmentCN("这个错过去你可以犯,但是现在再犯就不应该了")## [1] "这个" "错过" "去" "你" "可以" "犯" "但是" "现在" "再" "犯"
## [11] "就" "不" "应该" "了"
deleteWords("错过")
insertWords("过去")
segmentCN("这个错过去你可以犯,但是现在再犯就不应该了")## [1] "这个" "错" "过去" "你" "可以" "犯" "但是" "现在" "再" "犯"
## [11] "就" "不" "应该" "了"
# 安装卸载词典
# 在做分词处理时,可能会遇到一些比较精而专的文章,
# 专业词汇在词库里面并没有,这时候就需要去找相关的词典,
# 安装到R中。例如,在做新闻分析中,一些娱乐新闻里会有很多明星歌手的名字出现
# ,这些名字在做分词时,不会被识别为一个个词。此时可能需要添加一个名字的词典,
# 词典可以是自己建也可以从网上找。推荐从搜搜狗输入法的词库下载地址http://pinyin.sogou.com/dict/(Rwordseg的亮点)
# 可以选择需要的分类词典下载。
#这里我用到的一个词典names的下载地址:http://pinyin.sogou.com/dict/cate/index/429。搜狗词库
segmentCN("2017年的几部开年戏都出现了徐静的身影") ## [1] "2017年" "的" "几部" "开" "年" "戏" "都"
## [8] "出现" "了" "徐" "静" "的" "身影"
##安装搜狗词库
#installDict("C:/Users/Administrator.USER-20170417DX/Desktop/R文本挖掘/singer.scel", dictname ="names")
# segmentCN("2015年的几部开年戏都出现了徐静的身影")
# listDict() #查看词库
##删除自己添加的词库
# uninstallDict()
# listDict()
#===========================================================================================
#网上经典案例对某品牌官微做分词
#step1
#首先安装跟服装相关的词典,同样是从搜狗输入法的词库中下载的两个服饰类的词典,
#下载地址http://pinyin.sogou.com/dict/cate/index/397,这个地址下的前两个词库。
# installDict("D:\\R\\sources\\Dictionaries\\fushi.scel",dictname = "fushi")
# installDict("D:\\R\\sources\\Dictionaries\\Ali_fushi.scel",dictname = "alifushi")
# listDict()
#step2
####读文本数据
# hlzj <-readLines("d:\\R\\RWorkspace\\orgData.txt",encoding ="UTF-8")
# length(hlzj)
# 下一步是将数据读入R中,可以看到一共有1640条微博数据,注意数据的编码格式,
# readLines默认读取格式是gbk格式的,读取格式不对时会乱码。
#step3:接下来就是做分词了,要先去除数据中可能存在的数字和一些特殊符号,然后分词。
# hlzjTemp <- gsub("[0-90123456789 < > ~]","",hlzj)
# hlzjTemp <- segmentCN(hlzjTemp)
# hlzjTemp[1:2]
# 可以看到微博内容都已经被做过分词处理了,这个过程很简单,但实际上可能需要多次查看分词处理结果,
# 有些词库中不存在所以被截开了的词需要被添加进去,从而让分词效果达到最好。
#step4
# 去停词(一会我们会用tm包去停止词)
# 分词已经有结果了,但是分词的结果中存在很多像,“吧”,“吗”,“的”,“呢”这些无实际含义的语气词,或者是“即使”,
# “但是”这样的转折词,或者是一些符号,这样的词就叫做停词。要做进一步的分析可能需要去掉这些停词。
# 先自己整理一个停词表,这个停词表是我自己找的,包含一些常见的停词,然后根据实际内容中出现的一些无实际分析
# 意义的词语,就可以作为我们的停词表了,**网上能找到别人已经整理好的停词表**。
#stopwords<- unlist(read.table("D:\\R\\RWorkspace\\StopWords.txt",stringsAsFactors=F))
# stopwords[50:100]
#
# removeStopWords <- function(x,stopwords) {
# temp <- character(0)
# index <- 1
# xLen <- length(x)
# while (index <= xLen) {
# if (length(stopwords[stopwords==x[index]]) <1)
# temp<- c(temp,x[index])
# index <- index +1
# }
# temp
# }
#
# hlzjTemp2 <-lapply(hlzjTemp,removeStopWords,stopwords)
# hlzjTemp2[1:2]
#step5,6,7,8.....词云,模型等各种分析,见下面slides还是使用jieba分词吧
import sys
reload(sys)
sys.setdefaultencoding("UTF-8")
#1. 走进NLP
'''
概念
Natural Language Processing/Understanding,自然语言处理/理解
日常对话、办公写作、上网浏览
希望机器能像人一样去理解,以人类自然语言为载体的文本所包含的信息,并完成一些特定任务
内容
中文分词、词性标注、命名实体识别、关系抽取、关键词提取、信息抽取、依存分析、词嵌入……
应用
篇章理解、文本摘要、情感分析、知识图谱、文本翻译、问答系统、聊天机器人……
'''
#jieba分词
#pip install jieba
import jieba
seg_list = jieba.cut("我来到青岛联信集团", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list)) # 全模式
for item in seg_list:
print item
seg_list = jieba.cut("我来到青岛联信集团", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list)) # 精确模式
seg_list = jieba.cut("他来到了网易杭研大厦") # 默认是精确模式
print(", ".join(seg_list))
seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造") # 搜索引擎模式
print(", ".join(seg_list))
#关键词提取
import jieba.analyse
sentence=u"中国特色社会主义是我们党领导的伟大事业,全面推进党的建设新的伟大工程,是这一伟大事业取得胜利的关键所在。党坚强有力,事业才能兴旺发达,国家才能繁荣稳定,人民才能幸福安康。党的十八大以来,我们党坚持党要管党、从严治党,凝心聚力、直击积弊、扶正祛邪,党的建设开创新局面,党风政风呈现新气象。习近平总书记围绕从严管党治党提出一系列新的重要思想,为全面推进党的建设新的伟大工程进一步指明了方向。"
#基于TF-IDF:词频-反向文档频率(词频/文档频率,判断词重要成都)
jieba.analyse.extract_tags(sentence, topK=20, withWeight=False, allowPOS=()) #允许返回的词性
#基于TextRank:
jieba.analyse.textrank(sentence, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v'))
#词性标注
import jieba.posseg as pseg
words = pseg.cut("我爱北京天安门")
for word, flag in words:
print('%s, %s' % (word, flag))
'''
词性列表
1. 名词 (1个一类,7个二类,5个三类)
n 名词
nr 人名
nr1 汉语姓氏
nr2 汉语名字
nrj 日语人名
nrf 音译人名
ns 地名
nsf 音译地名
nt 机构团体名
nz 其它专名
nl 名词性惯用语
ng 名词性语素
2. 时间词(1个一类,1个二类)
t 时间词
tg 时间词性语素
3. 处所词(1个一类)
s 处所词 (家中、门外、境内、西方……)
4. 方位词(1个一类)
f 方位词
5. 动词(1个一类,9个二类)
v 动词
vd 副动词
vn 名动词
vshi 动词“是”
vyou 动词“有”
vf 趋向动词
vx 形式动词
vi 不及物动词(内动词)
vl 动词性惯用语
vg 动词性语素
6. 形容词(1个一类,4个二类)
a 形容词
ad 副形词
an 名形词
ag 形容词性语素
al 形容词性惯用语
7. 区别词(1个一类,2个二类)
b 区别词 (主要、整个、所有……)
bl 区别词性惯用语
8. 状态词(1个一类)
z 状态词
9. 代词(1个一类,4个二类,6个三类)
r 代词
rr 人称代词
rz 指示代词
rzt 时间指示代词
rzs 处所指示代词
rzv 谓词性指示代词
ry 疑问代词
ryt 时间疑问代词
rys 处所疑问代词
ryv 谓词性疑问代词
rg 代词性语素
10. 数词(1个一类,1个二类)
m 数词
mq 数量词
11. 量词(1个一类,2个二类)
q 量词
qv 动量词
qt 时量词
12. 副词(1个一类)
d 副词
13. 介词(1个一类,2个二类)
p 介词
pba 介词“把”
pbei 介词“被”
14. 连词(1个一类,1个二类)
c 连词
cc 并列连词
15. 助词(1个一类,15个二类)
u 助词
uzhe 着
ule 了 喽
uguo 过
ude1 的 底
ude2 地
ude3 得
usuo 所
udeng 等 等等 云云
uyy 一样 一般 似的 般
udh 的话
uls 来讲 来说 而言 说来
uzhi 之
ulian 连 (“连小学生都会”)
16. 叹词(1个一类)
e 叹词
17. 语气词(1个一类)
y 语气词(delete yg)
18. 拟声词(1个一类)
o 拟声词
19. 前缀(1个一类)
h 前缀
20. 后缀(1个一类)
k 后缀
21. 字符串(1个一类,2个二类)
x 字符串
xx 非语素字
xu 网址URL
22. 标点符号(1个一类,16个二类)
w 标点符号
wkz 左括号,全角:( 〔 [ { 《 【 〖 〈 半角:( [ { <
wky 右括号,全角:) 〕 ] } 》 】 〗 〉 半角: ) ] { >
wyz 左引号,全角:“ ‘ 『
wyy 右引号,全角:” ’ 』
wj 句号,全角:。
ww 问号,全角:? 半角:?
wt 叹号,全角:! 半角:!
wd 逗号,全角:, 半角:,
wf 分号,全角:; 半角: ;
wn 顿号,全角:、
wm 冒号,全角:: 半角: :
ws 省略号,全角:…… …
wp 破折号,全角:—— -- ——- 半角:--- ----
wb 百分号千分号,全角:% ‰ 半角:%
wh 单位符号,全角:¥ $ £ ° ℃ 半角:$
https://github.com/DataXujing/jieba(Python jieba项目,详细文档,frok from 原作者)
'''## Building prefix dict from the default dictionary ...
## Loading model from cache c:\users\admini~1.use\appdata\local\temp\jieba.cache
## Loading model cost 0.316 seconds.
## Prefix dict has been built succesfully.
## Full Mode: 鎴㤼㸱/ 鏉ュ埌/ 闈掑矝/ 鑱斾俊/ 闆嗗洟
## Default Mode: 鎴㤼㸱/ 鏉ュ埌/ 闈掑矝/ 鑱斾俊/ 闆嗗洟
## 浠㤼㸶, 鏉ュ埌, 浜㠼㸶, 缃戞槗, 鏉爺, 澶у帵
## 灏忔槑, 纭曞+, 姣曚笟, 浜㠼㹥, 涓浗, 绉戝, 瀛﹂櫌, 绉戝闄愼㸲, 涓浗绉戝闄愼㸲, 璁$畻, 璁$畻鎵€, 锛㠼㹣, 鍚㠼㹥, 鍦愼㸸, 鏃ユ湰, 浜兘, 澶у, 鏃ユ湰浜兘澶у, 娣遍€愼㸰
## 鎴㤼㸱, r
## 鐖戼㸱, v
## 鍖椾含, ns
## 澶╁畨闂愼㸸, ns
本部分主要涉及R 语言环境中tm 包的使用。 使用R 里面tm 包进行文本挖掘,对于中文环境还有一些包来处理中文字符。关于tm 包 最早发表于Journal of Statistical Software [Feinerer et al., 2008],而关于text mining in R 最 早表在R News [Feinerer, 2008]。
在tm 中主要的管理文件的结构被称为语料库(Corpus),代表了一系列的文档集合。语 料库是一个概要性的概念,在这里分为动态语料库(Volatile Corpus,作为R 对象保存在内存 中)和静态语料库(Permanent Corpus,R 外部保存)。 在语料库构成中,x 必须有一个说明资料来源(input location)的源对象(Source Object)。 我们可以看一下tm 中Corpus(或VCorpus)函数的用法,对于动态语料库:
# Corpus(x,
# readerControl = list(reader = x$DefaultReader, language = "en"),
# ...)
# 在tm 中静态语料库也是可以处理的,但需要使用filehash 包来支持:
# PCorpus(x,
# readerControl = list(reader = x$DefaultReader, language = "en"),
# dbControl = list(dbName = "", dbType = "DB1"),
# ...)
# 对于这些资料来源(即x),tm 包提供了一些相关的函数,比如
# . DirSource:处理目录
# . VectorSource:由文档构成的向量
# . DataframeSource:数据框,就像CSV 文件
# 第二个参数是readerControl,这里必须声明reader 和language 两个内容。第一个reader
# 是指从资料源创立的文本文件。tm 包提供了一系列的函数支持(比如,readPlain(),
# readGmane(), readRCV1(), readReut21578XMLasPlain(), readPDF(), readDOC() 等)。可以
# 使用getReaders() 获得这些函数的列表。对于每一类源,都会有自己默认的reader。比如对
# DirSource 来说,默认读入输入文件并把内容解释为文本。第二个language 就比较简单了,即
# 字符集,比如可能是UTF-8 字符集。
# 在使用静态语料库条件下,会涉及第三个参数dbControl,它用来声明R 内存对象外的资
# 料来源(比如数据库)。dbType 控制了包filehash 支持的数据库类型。数据库支持可以有效的
# 减少对内存的要求,但数据的访问会受到硬盘的读写能力限制。
# 比如,txt 目录下的包含拉丁字符的纯文本,内容是罗马诗人奥维德Ovid 的诗集,可以这样读进来#
# library(NLP)
# library(tm)
# vignette("tm")
#
# txt <- system.file("texts", "txt", package = "tm")
# (ovid <- Corpus(DirSource(txt),readerControl = list(language = "lat")))
#
# #当然同样也可以从字符向量创建语料库:
# docs <- c("This is a text.", "This another one.")
# Corpus(VectorSource(docs))
#
# #最后我们根据路透社文档创建一个语料库,用于后续示例:
#
# reut21578 <- system.file("texts", "crude", package = "tm")
# reuters <- Corpus(DirSource(reut21578),readerControl = list(reader = readReut21578XML))
# reuters假如你在R 中创建了一个语料库,但硬盘上并没有,那么使用writeCorpus() 函数保存。 这时,在工作目录下会生成与语料库对应的多个纯文本文件。
#writeCorpus(ovid)对于print() 和summary() 函数依然有效,但源信息被隐藏(可以想象一下每个语料库包 含了大量的文本,就像数据库一样)。summary() 函数会提供更多的元数据(meta data)信息, 完整信息的提取需要使用inspect(),比如: 对于单个文档的提取需要使用[[,当然既可以通过位置也可以通过名称:
# inspect(ovid[1:2])
# identical(ovid[[2]], ovid[["ovid_2.txt"]])一旦创建了语料库,后续文档修改则不可避免,比如填充、停止词去除。在tm 包里,这 些函数都归到信息转化里面,其主要函数就是tm_map(),这个函数可以通过maps 方式将转 化函数实施到每一个语料上。
在reuters 这个语料库中保存的是XML 格式的文本,XML 格式文本没有分析的意义,我 们只需要使用其中的文本内容。这个操作可以使用PlainTextDocument() 函数来实现:
#reuters <- tm_map(reuters, PlainTextDocument)注意,另外一种方式就是使用readReut21578XMLasPlain 读取方式,那么在第一步即为纯 文本格式。
#reuters <- tm_map(reuters, stripWhitespace)#reuters <- tm_map(reuters, tolower)
#更广泛的字符操作请参考gsub#reuters <- tm_map(reuters, removeWords, stopwords("english"))
#tm包中没有中文停止次,可以使用tmcn包中的stopwordCN函数
#d.corpus <- tm_map(d.corpus, removeWords, stopwordsCN())removeNumbers
# tm_map(reuters, stemDocument)
# # 可以使用 getTransformations()函数查看所有的字符处理方式
# getTransformations()# # tm_reduce(x, tmFuns, ...)
#
# data(crude)
# crude[[1]]
# skip_words<-function(x) removeWords(x,c("it","the"))
#
#
# funcs<-list(tolower,skip_words,removePunctuation,stripWhitespace)
#
# tm_map(crude,FUN=tm_reduce,tmFuns=funcs)[[1]]有时候,我们需要选取给定条件下的文档。tm_filter 函数即为这个目所设计。sFilter 适 应于一般情况下的用户自定义过滤情况:它整合了适用于元数据的的最小查询语句。假如需 要找出ID 等于237,表头(heading)包含“INDONESIA SEEN AT CROSSROADS OVER ECONOMIC CHANGE” 字符的文本本舰。
# query <- "id == '237' & heading == 'INDONESIA SEEN AT CROSSROADS OVER ECONOMIC CHANGE'"
#
# tm_filter(reuters, FUN = sFilter, query)
# #全文过滤 这两个函数没找到??
# tm_filter(reuters, pattern = "company")
# tm_filter(reuters, FUN = searchFullText, "company")
# data("crude")
# tm_filter(crude, FUN = function(x) any(grep("co[m]?pany", content(x))))元数据是为了标记语料库的附加信息,最简单的使用方式就是调用meta() 函数。文档会被 预先被定义一些属性,比如作者信息,但也可能是任意自定义的元数据标签。这些附加的元数 据标签都是独立的附加在单个文档上。从语料库的视角上看,这些元数据标签被独立的存储在 每个文档上。除了meta() 函数外,DublinCore() 函数提供了一套介于Simple Dublin Core 元 数据和tm 元数据之间的映射机制,用于获得或设置文档的元数据信息。比如:
# DublinCore(crude[[1]], tag = "creator") <- "xujing"
# DublinCore(crude[[1]])#这个是按照都柏林核心的国际标准显示
# meta(crude[[1]])#显示第一个文件的元素信息上面讲到的一些示例只是针对于文档级别的元数据管理。实际上在tm 包元数据管理体系 中,元数据标签对应了两个level:对于语料库(corpus)级别:文档的集合,单个文档的元数据。 后一种元数据的使用主要是由于一些性能原因,或者是由于一些分析上的需要,比如要对文档 进行分类(注意是classification),分类的结果直接和每个文档的标记有关系。
# meta(crude, tag = "test", type = "corpus") <- "test meta"
# meta(crude, type = "corpus")
# meta(crude, "foo") <- letters[1:20]
# meta(crude)对于语料库来说,其标准操作和函数和R 的一般函数非常类似,比如 [, [<-, [[, [[<-, c(), lapply() 对于像c()这这个函数即是连接多个语料库的意思。连接之后,元数据信息也会被更新。
文档词条矩阵是整个tm包乃至现阶段所有R语言文本挖掘相关包的最基础对象 文本挖掘的要创建词条-文档关系矩阵,它是后续构建模型的基础。假设我们有两个文档 分别是text mining is fun 和a text is a sequence of words,那么对应的矩阵为:
在tm包里,根据词条、文档分别作为行、列或反之,对应有TermDocumentMatrix 和 DocumentTermMatrix 两类稀疏矩阵,下面我们看一个例子:
# dtm <-DocumentTermMatrix(reuters)
# inspect(dtm[1:5,100:105])
# data("crude")
# tdm <- TermDocumentMatrix(crude,
# control = list(removePunctuation = TRUE,
# stopwords = TRUE))
# dtm <- DocumentTermMatrix(crude,
# control = list(weighting =
# function(x)
# weightTfIdf(x, normalize =
# FALSE),
# stopwords = TRUE))
# inspect(tdm[202:205, 1:5])
# inspect(tdm[c("price", "prices", "texas"), c("127", "144", "191", "194")])
# inspect(dtm[1:5, 273:276])
#
# s <- SimpleCorpus(VectorSource(unlist(lapply(crude, as.character))))
# m <- TermDocumentMatrix(s,
# control = list(removeNumbers = TRUE,
# stopwords = TRUE,
# stemming = TRUE))
# inspect(m[c("price", "texa"), c("127", "144", "191", "194")])实际上对于矩阵的操作R 有大量的函数(比如聚类、分类算法等)支持,但这个包还是提 供了一些常用的函数支持。假如需要找出发生5 次以上的条目,可以使用findFreqTerms()函 数:
#findFreqTerms(dtm, 5)或者找到相关性,比如对于opec,找到相关系数在0.8 以上的条目,使用findAssocs(): 这个函数还可以接受一般的矩阵。对于一般矩阵,会将矩阵直接转化为相关阵,这种方式可以 实现不同的相关计算方式。
#findAssocs(dtm, "opec", 0.8)词条-文档关系矩阵一般都是非常庞大的数据集,因此这里提供了一种删减稀疏条目的方 法,比如有些条目尽在很少的文档中出现。一般来说,这样做不会对矩阵的信息继承带来显著 的影响。这个函数去除了低于40% 的稀疏条目项。
#inspect(removeSparseTerms(dtm, 0.4))字典是一个字符集和。经常用于在文本挖掘中展现相关的词条时。使用Dictionary() 函数 实现,可以看示例:
#(d <- Dictionary(c("prices", "crude", "oil")))当将字典传递到DocumentTermMatrix() 以后,生成的矩阵会根据字典提取计算,而不是漫无 目的地全部提取。
#inspect(DocumentTermMatrix(reuters, list(dictionary = d)))在得到TermDocument 矩阵以后,基本上所有的数据挖掘算法都可以使用,如Cluster、 Classification、Regression 等,甚至Apriori、SNA 等技术。关于模型会在后文中介绍。
正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代 码。正则并不是本文档的讨论重点,这里有一个正则表达式30 分钟入门教程,但需要提 醒一下,30 分钟学会是幌子,正则表达式的学习是陡峭而漫长的。
tm包的缺点
继续在探索的路上……
核心包:install.packages(“tmcn”,repos=“http://R-Forge.R-project.org”)
CRF扩展包:install.packages(“tmcn.crfpp”,repos=“http://R-Forge.R-project.org”)
word2vec: install.packages(“tmcn.word2vec”,repos=“http://R-Forge.R-project.org”)
#-------------GBK字符集------------------
library(tmcn)## # tmcn Version: 0.2-8
##
## Attaching package: 'tmcn'
## The following objects are masked from 'package:Rwordseg':
##
## insertWords, segmentCN
data(GBK)
head(GBK)## GBK py0 py Radical Stroke_Num_Radical
## 1 吖 a ā yā 口 3
## 2 阿 a ā ɑ ē 阝 2
## 3 啊 a ɑ á à ǎ ā 口 3
## 4 锕 a ā 钅 5
## 5 錒 a ā 釒 8
## 6 嗄 a á shà 口 3
## Stroke_Order Structure Freq
## 1 丨フ一丶ノ丨 左右 26
## 2 フ丨一丨フ一丨 左右 526031
## 3 丨フ一フ丨一丨フ一丨 左中右 53936
## 4 ノ一一一フフ丨一丨フ一丨 左中右 3
## 5 ノ丶一一丨丶ノ一フ丨一丨フ一丨 左右 0
## 6 丨フ一一ノ丨フ一一一ノフ丶 左右 11
#-------------字符编码识别----------------
txt1 <- c("\u4E2D\u56FDR\u8BED\u8A00\u4F1A\u8BAE")
txt2 <- iconv(txt1, "UTF-8", "GBK")
txt3 <- txt1
Encoding(txt3) <- "GBK"
isUTF8(txt1)## [1] FALSE
isGBK(txt2)## [1] TRUE
#------------UTF-8转换-----------------
#setCN()
txt1 <- c("我爱联信")
toUTF8(txt1)## [1] "我爱联信"
catUTF8(txt1)## \u6211\u7231\u8054\u4FE1
revUTF8("\u6211\u7231\u8054\u4FE1")## [1] "我爱联信"
#------------中文字符转换---------------
txt1 <- c("徐静爱联信")
toTrad(txt1)## [1] "徐靜愛聯信"
toTrad("徐靜愛聯信", rev = TRUE)## [1] "徐静爱联信"
toPinyin(txt1, capitalize = TRUE)## [1] "XuJingAiLianXin"
#-----------字符处理------------------
txt1 <- c("\t(x1)a(aa2)a ", " bb(bb)")
text11<-strextract(txt1, "\\([^)]*\\)")
unlist(text11)## [1] "(x1)" "(aa2)" "(bb)"
strstrip(c("\taaaa ", " bbbb "))## [1] "aaaa" "bbbb"
#---------类似于tm的操作--------
# createDTM(string, tokenize = NULL, removePunctuation = TRUE, removeStopwords = TRUE)
# createTDM(string, tokenize = NULL, removePunctuation = TRUE, removeStopwords = TRUE)
# segmentCN(strwords, package = c("jiebaR", "Rwordseg"), nature = FALSE,
# nosymbol = TRUE, returnType = c("vector", "tm"))
# insertWords(inswords, package = c("jiebaR", "Rwordseg"))路漫漫其修远兮,吾将上下而求索。
下面开始介绍模型
聚类算法是针对数值型变量的,先要将文本数据转换为matrix—数据矩阵。 过程如下,这里需要用到tm软件包,先安装该软件包并加载。 tm包中的Corpus()方法就是用来将文本转换为语料库的方法。 DocumentTermMatrix()方法,显然就是将语料库转换为文档-词条矩阵, 然后再将文档-词条矩阵转换为普通矩阵(这个过程在tm包,tmcn包我们已经完成)
把文本转化成词条-文档关系矩阵或文档-词条关系矩阵后就可以使用机器学习中一些常用的 无监督学习方法进行文本挖掘,当然也有一些专门处理文本聚类的模型:比如前文提到的skmeans包 提供了几种模糊KMeans的算法,textcat包可以进行基于n-gram短语的文本聚类,movMF提供了一种基于 概率模型(基于vMF分布)的文本聚类方法,此外kernlab包也提供了一些kernal机器学习的方法进行文本聚类。fpc软件包中的dbscan()方法可以实现dbscan聚类,还有其他的聚类方法,就不一一介绍了,优劣取舍要在实际应用中去控制了。感兴趣大家可以研究一下,有统计学,数学基础作为铺垫的学生学习起来就相当容易。
下面我们看一些在文本挖掘中常用的文本聚类方法
在无监督学习中最常用的聚类方法,用到各种距离的计算,数据注意是连续性的数据,且注意量纲的影响, 在学习的过程中注意K的选取,聚类后的数据在业务层面上注意合理的解释。
# kmeans(x, centers, iter.max = 10, nstart = 1,
# algorithm = c("Hartigan-Wong", "Lloyd", "Forgy",
# "MacQueen"), trace=FALSE)
# ## S3 method for class 'kmeans'
# fitted(object, method = c("centers", "classes"), ...)
# #model
# k <- kopt #选择最优的K
# kmeansRes <- kmeans(numeric-matrix-data,k) #k是聚类数
# mode(kmeansRes) #kmeansRes的内容
# names(kmeansRes)
# (kmeansRes)
#
# hlzj.kmeansRes <- list(content=hlzj,type=kmeansRes$cluster)
# write.csv(hlzj.kmeansRes,"hlzj_kmeansRes.csv")
# fix(hlzj.kmeansRes)
#选择最优的k
# errs<-numeric(n0)
# for ( k in 1:n0)
# {
# models<-kmeans(numeric-matrix,center=k)
# errs[k]<-models$betweens/model$totss
# }
# errs<- round(errs,2)
#
# plot(1:n0,errs,type='b',main="Choosing the Optimal Number of Cluster",xlab="Number of cluster:1 to n0",
# ylab="betweenss/totss")
#
# points(kopts,errs[opts],pch=16)
#
# legned(kopts,errs[opts],paste("(opts,",sprintf("%.1f%%",errs[opts]*100),")",sep=""),bty="n",xjust=0.3,cex=0.8)# x=runif(10)
# y=runif(10)
# S=cbind(x,y) #得到2维的数组
# rownames(S)=paste("Name",1:10,"") #赋予名称,便于识别分类
# out.dist=dist(S,method="euclidean") #数值变距离
# 注释:在聚类中求两点的距离有:
# 绝对距离:manhattan
# 欧氏距离:euclidean 默认
# 闵科夫斯基距离:minkowski
# 切比雪夫距离:chebyshev
# 马氏距离:mahalanobis
# 蓝氏距离:canberra
# out.hclust=hclust(out.dist,method="complete") #根据距离聚类
# 注释:聚类中集合之间的距离:
# 类平均法:average
# 重心法:centroid
# 中间距离法:median
# 最长距离法:complete 默认
# 最短距离法:single
# 离差平方和法:ward
# 密度估计法:density
# plclust(out.hclust) #对结果画图 如图1
# rect.hclust(out.hclust,k=3) #用矩形画出分为3类的区域 如图2
# out.id=cutree(out.hclust,k=3) #得到分为3类的数值
# out.id
#
# table(out.id,paste("Name",1:10,"")) #以向量的方式分辨名称对应的类
# d <- dist(numeric-matrix,method="euclidean")
# hclustRes <- hclust(d,method="complete")
# hclustRes.type <- cutree(hclustRes,k=5) #按聚类结果分5个类别
# length(hclustRes.type)
# hclustRes.type[1:10]
#
# hclustRes <- list(content=hlzj,type=hclustRes.type)
# hclustRes <- as.data.frame(hclustRes)
# fix(hlzj.hclustRes)
##example:
#we use four methods to solve this problem, further,we use four distance.But we do only
#one method.
# rm(list=ls(all=TRUE))
#
# Y=read.table('C:/Documents and Settings/Administrator/中兴/test.txt',sep=',')
#
# d=dist(Y[,-4])
# Hc1=hclust(d,method="single")
# # Hc2=hclust(d,"complete")
# # Hc3=hclust(d,"median")
# # Hc4=hclust(d,"ward.D")
#
# #opar=par(mfrow=c(2,2))
# plot(Hc1,hang=-1)
# kopt=
# re1=rect.hclust(Hc1,kopt);
# # plot(Hc1,hang=-1)
# # re2=rect.hclust(Hc2,k=2);
# # plot(Hc1,hang=-1)
# # re3=rect.hclust(Hc3,k=2);
# # plot(Hc1,hang=-1)
# # re4=rect.hclust(Hc4,k=2);
# #par(opar)
#
#
# id1=cutree(Hc1,kopt)
# # id2=cutree(Hc2,3)
# # id3=cutree(Hc3,3)
# # id4=cutree(Hc4,3)
#
#
# # Sresult1=table(id1,Y$V4)
# # Sresult2=table(id2,Y$V4)
# # Sresult3=table(id3,Y$V4)
# # Sresult4=table(id4,Y$V4)
#
# Sresult1=data.frame(id1,Y$V4);
# write.csv(Sresult1,file="C:/Documents and Settings/Sresult1.csv");
#
#
# ipn='苏N31157' #inputplate numbers here!!!!!!!
#
# cpn=as.numeric(Sresult1[which(Sresult1[,2]==ipn),1] )
#
# spn=Sresult1[which(id1==cpn),2]
#
# spn
#
# write.table(spn,file="C:/Documents and Settings/spn S result.txt")
#cutree函数对hclust()的聚类结果进行剪枝
#cutree(tree,k=NULL,h=NULL)
#rect.hclust(),可以在plot()形成的系谱图中框定指定哪个类别的样本分支
# rect.hclust(tree, k = NULL, which = NULL, x = NULL, h = NULL,
# border = 2, cluster = NULL)软件包kernlab中的specc()方法可以实现kernel聚类算法。查看这个包的说明文档help(kernlab), 也可以输入??kernel查看帮助文档能看到。 网上能够找到的翻译后的方法说明http://www.biostatistic.net/thread-49108-1-1.html。 具体实现过程如下:
# library(kernlab)
#
# stringkern <-stringdot(type="string")
# kernelRes <-specc(matrix,centers=k,kernel=stringkern)
# mode(kernelRes)
#
# kernelRes<-list(cotent=hlzj,type=temp[1:1639] )
# kernelRes <-as.data.frame(hlzj.kernelRes)
# fix(kernelRes)与其他聚类算法比较,k-means类算法在处理大数据时效率较高,但他以距离作为类间相似性的度量 不适合离散性的数据。传统方法将离散数据转化为数值型,其问题域的顺序遭到破坏,往往无法得到有意义的结果。1997年,Huang提出了k-propotype的算法,可以将数字与离散变量混合的数据进行聚类。同年,Huang提出了了k-modes算法(Huang 1997b),其实是k-propotypes算法的一个简化,只与离散属性相关,该算法可处理 大数据集。
# library(klaR)
# library(readr)
#
# datakmode<-read_csv("data/data_kmode.csv")
#
# names(datakmode)<-c("ajbh",'age','sex','qkze','ajlx','pici')
#
# datakmode1<-as.matrix(datakmode[,-1])
#
# err<-numeric(15) #保存类内相异度量和
#
# for (i in 1:15)
# {
# cl<-kmodes(datakmode1, i, iter.max = 100, weighted = FALSE)
# err[i]<-sum(cl$withindiff)
#
# }
#
# scaleerr<-err/sum(err)
# plot(1:15,scaleerr,type="b",main="Choosing the Optimal Number of Cluster",
# xlab="Number of Cluster",ylab="类内相异度量和")
# points(6,scaleerr[6],pch=16,col='red')
# legend(6,scaleerr[6],paste("(6",sprintf("%.2f%%",scaleerr[6]*100),")",sep=" "),bty="n",xjust=0.3,cex=0.8)
#
# cl6<-kmodes(datakmode1,6,iter.max=100,weighted=F)
#
# (cl6)k-prototypes算法在聚类过程中计算数值属性的相异性度量sn和离散属性的相异性度量sc,并设置了一个 避免两个属性失衡的权重。
#' ####这是Python代码,以前想用R做该方法没找到,不知现在有没有,如果大家在R中找到了这个包,一定告诉我。。。。。。
#'
#' # -*- coding: utf-8 -*-
#' """
#' Created on Mon Oct 10 14:43:38 2016
#'
#' @author: xujing
#'
#' """
#'
#' """
#' K-prototypes clustering for mixed categorical and numerical data
#' """
#'
#'
#'
#' from collections import defaultdict
#'
#' import numpy as np
#' from scipy import sparse
#' from sklearn.utils.validation import check_array
#'
#' from . import kmodes
#' from .util import get_max_value_key, encode_features
#' from .util.dissim import matching_dissim, euclidean_dissim
#'
#'
#' def move_point_num(point, ipoint, to_clust, from_clust, cl_attr_sum, membship):
#' """Move point between clusters, numerical attributes."""
#' membship[to_clust, ipoint] = 1
#' membship[from_clust, ipoint] = 0
#' # Update sum of attributes in cluster.
#' for iattr, curattr in enumerate(point):
#' cl_attr_sum[to_clust][iattr] += curattr
#' cl_attr_sum[from_clust][iattr] -= curattr
#' return cl_attr_sum, membship
#'
#'
#' def _split_num_cat(X, categorical):
#' """Extract numerical and categorical columns.
#' Convert to numpy arrays, if needed.
#' :param X: Feature matrix
#' :param categorical: Indices of categorical columns
#' """
#' Xnum = np.asanyarray(X[:, [ii for ii in range(X.shape[1])
#' if ii not in categorical]]).astype(np.float64)
#' Xcat = np.asanyarray(X[:, categorical])
#' return Xnum, Xcat
#'
#'
#' def _labels_cost(Xnum, Xcat, centroids, gamma):
#' """Calculate labels and cost function given a matrix of points and
#' a list of centroids for the k-prototypes algorithm.
#' """
#'
#' npoints = Xnum.shape[0]
#' Xnum = check_array(Xnum)
#'
#' cost = 0.
#' labels = np.empty(npoints, dtype=np.uint8)
#' for ipoint in range(npoints):
#' # Numerical cost = sum of Euclidean distances
#' num_costs = euclidean_dissim(centroids[0], Xnum[ipoint])
#' cat_costs = matching_dissim(centroids[1], Xcat[ipoint])
#' # Gamma relates the categorical cost to the numerical cost.
#' tot_costs = num_costs + gamma * cat_costs
#' clust = np.argmin(tot_costs)
#' labels[ipoint] = clust
#' cost += tot_costs[clust]
#'
#' return labels, cost
#'
#'
#' def _k_prototypes_iter(Xnum, Xcat, centroids, cl_attr_sum, cl_attr_freq,
#' membship, gamma):
#' """Single iteration of the k-prototypes algorithm"""
#' moves = 0
#' for ipoint in range(Xnum.shape[0]):
#' clust = np.argmin(
#' euclidean_dissim(centroids[0], Xnum[ipoint]) +
#' gamma * matching_dissim(centroids[1], Xcat[ipoint])
#' )
#' if membship[clust, ipoint]:
#' # Point is already in its right place.
#' continue
#'
#' # Move point, and update old/new cluster frequencies and centroids.
#' moves += 1
#' old_clust = np.argwhere(membship[:, ipoint])[0][0]
#'
#' cl_attr_sum, membship = move_point_num(
#' Xnum[ipoint], ipoint, clust, old_clust, cl_attr_sum, membship
#' )
#' cl_attr_freq, membship, centroids[1] = kmodes.move_point_cat(
#' Xcat[ipoint], ipoint, clust, old_clust,
#' cl_attr_freq, membship, centroids[1]
#' )
#'
#' # Update old and new centroids for numerical attributes using the mean
#' # of all values
#' for iattr in range(len(Xnum[ipoint])):
#' for curc in (clust, old_clust):
#' if sum(membship[curc, :]):
#' centroids[0][curc, iattr] = \
#' cl_attr_sum[curc, iattr] / sum(membship[curc, :])
#' else:
#' centroids[0][curc, iattr] = 0.
#'
#' # In case of an empty cluster, reinitialize with a random point
#' # from largest cluster.
#' if sum(membship[old_clust, :]) == 0:
#' from_clust = membship.sum(axis=1).argmax()
#' choices = \
#' [ii for ii, ch in enumerate(membship[from_clust, :]) if ch]
#' rindx = np.random.choice(choices)
#'
#' cl_attr_freq, membship = move_point_num(
#' Xnum[rindx], rindx, old_clust, from_clust, cl_attr_sum, membship
#' )
#' cl_attr_freq, membship, centroids[1] = kmodes.move_point_cat(
#' Xcat[rindx], rindx, old_clust, from_clust,
#' cl_attr_freq, membship, centroids[1]
#' )
#'
#' return centroids, moves
#'
#'
#' def k_prototypes(X, categorical, n_clusters, gamma, init, n_init,
#' max_iter, verbose):
#' """k-prototypes algorithm"""
#'
#' if sparse.issparse(X):
#' raise TypeError("k-prototypes does not support sparse data.")
#'
#' if categorical is None or not categorical and verbose:
#' print("No categorical data selected, effectively doing k-means.")
#' if isinstance(categorical, int):
#' categorical = [categorical]
#' assert len(categorical) != X.shape[1], \
#' "All columns are categorical, use k-modes instead of k-prototypes."
#' assert max(categorical) < X.shape[1], \
#' "Categorical index larger than number of columns."
#'
#' ncatattrs = len(categorical)
#' nnumattrs = X.shape[1] - ncatattrs
#' npoints = X.shape[0]
#' assert n_clusters < npoints, "More clusters than data points?"
#'
#' Xnum, Xcat = _split_num_cat(X, categorical)
#' Xnum, Xcat = check_array(Xnum), check_array(Xcat, dtype=None)
#'
#' # Convert the categorical values in Xcat to integers for speed.
#' # Based on the unique values in Xcat, we can make a mapping to achieve this.
#' Xcat, enc_map = encode_features(Xcat)
#'
#' # Estimate a good value for gamma, which determines the weighing of
#' # categorical values in clusters (see Huang [1997]).
#' if gamma is None:
#' gamma = 0.5 * Xnum.std()
#'
#' all_centroids = []
#' all_labels = []
#' all_costs = []
#' all_n_iters = []
#' for init_no in range(n_init):
#'
#' # For numerical part of initialization, we don't have a guarantee
#' # that there is not an empty cluster, so we need to retry until
#' # there is none.
#' while True:
#' # _____ INIT _____
#' if verbose:
#' print("Init: initializing centroids")
#' if isinstance(init, str) and init == 'Huang':
#' centroids = kmodes.init_huang(Xcat, n_clusters)
#' elif isinstance(init, str) and init == 'Cao':
#' centroids = kmodes.init_cao(Xcat, n_clusters)
#' elif isinstance(init, str) and init == 'random':
#' seeds = np.random.choice(range(npoints), n_clusters)
#' centroids = Xcat[seeds]
#' elif isinstance(init, list):
#' assert init[0].shape[0] == n_clusters, \
#' "Incorrect number of initial numerical centroids in init."
#' assert init[0].shape[1] == nnumattrs, \
#' "Incorrect number of numerical attributes in init"
#' assert init[1].shape[0] == n_clusters, \
#' "Incorrect number of initial categorical centroids in init."
#' assert init[1].shape[1] == ncatattrs, \
#' "Incorrect number of categorical attributes in init"
#' centroids = [np.asarray(init[0], dtype=np.float64),
#' np.asarray(init[1], dtype=np.uint8)]
#' else:
#' raise NotImplementedError
#'
#' if not isinstance(init, list):
#' # Numerical is initialized by drawing from normal distribution,
#' # categorical following the k-modes methods.
#' meanx = np.mean(Xnum, axis=0)
#' stdx = np.std(Xnum, axis=0)
#' centroids = [
#' meanx + np.random.randn(n_clusters, nnumattrs) * stdx,
#' centroids
#' ]
#'
#' if verbose:
#' print("Init: initializing clusters")
#' membship = np.zeros((n_clusters, npoints), dtype=np.uint8)
#' # Keep track of the sum of attribute values per cluster so that we
#' # can do k-means on the numerical attributes.
#' cl_attr_sum = np.zeros((n_clusters, nnumattrs), dtype=np.float64)
#' # cl_attr_freq is a list of lists with dictionaries that contain
#' # the frequencies of values per cluster and attribute.
#' cl_attr_freq = [[defaultdict(int) for _ in range(ncatattrs)]
#' for _ in range(n_clusters)]
#' for ipoint in range(npoints):
#' # Initial assignment to clusters
#' clust = np.argmin(
#' euclidean_dissim(centroids[0], Xnum[ipoint]) +
#' gamma * matching_dissim(centroids[1], Xcat[ipoint])
#' )
#' membship[clust, ipoint] = 1
#' # Count attribute values per cluster.
#' for iattr, curattr in enumerate(Xnum[ipoint]):
#' cl_attr_sum[clust, iattr] += curattr
#' for iattr, curattr in enumerate(Xcat[ipoint]):
#' cl_attr_freq[clust][iattr][curattr] += 1
#'
#' # If no empty clusters, then consider initialization finalized.
#' if membship.sum(axis=1).min() > 0:
#' break
#'
#' # Perform an initial centroid update.
#' for ik in range(n_clusters):
#' for iattr in range(nnumattrs):
#' centroids[0][ik, iattr] = \
#' cl_attr_sum[ik, iattr] / sum(membship[ik, :])
#' for iattr in range(ncatattrs):
#' centroids[1][ik, iattr] = \
#' get_max_value_key(cl_attr_freq[ik][iattr])
#'
#' # _____ ITERATION _____
#' if verbose:
#' print("Starting iterations...")
#' itr = 0
#' converged = False
#' cost = np.Inf
#' while itr <= max_iter and not converged:
#' itr += 1
#' centroids, moves = _k_prototypes_iter(
#' Xnum, Xcat, centroids, cl_attr_sum, cl_attr_freq, membship, gamma
#' )
#'
#' # All points seen in this iteration
#' labels, ncost = _labels_cost(Xnum, Xcat, centroids, gamma)
#' converged = (moves == 0) or (ncost >= cost)
#' cost = ncost
#' if verbose:
#' print("Run: {}, iteration: {}/{}, moves: {}, ncost: {}"
#' .format(init_no + 1, itr, max_iter, moves, ncost))
#'
#' # Store results of current run.
#' all_centroids.append(centroids)
#' all_labels.append(labels)
#' all_costs.append(cost)
#' all_n_iters.append(itr)
#'
#' best = np.argmin(all_costs)
#' if n_init > 1 and verbose:
#' print("Best run was number {}".format(best + 1))
#'
#' # Note: return gamma in case it was automatically determined.
#' return all_centroids[best], all_labels[best], all_costs[best], \
#' all_n_iters[best], gamma, enc_map
#'
#'
#' class KPrototypes(kmodes.KModes):
#' """k-protoypes clustering algorithm for mixed numerical/categorical data.
#' Parameters
#' -----------
#' n_clusters : int, optional, default: 8
#' The number of clusters to form as well as the number of
#' centroids to generate.
#' gamma : float, default: None
#' Weighing factor that determines relative importance of numerical vs.
#' categorical attributes (see discussion in Huang [1997]). By default,
#' automatically calculated from data.
#' max_iter : int, default: 300
#' Maximum number of iterations of the k-modes algorithm for a
#' single run.
#' n_init : int, default: 10
#' Number of time the k-modes algorithm will be run with different
#' centroid seeds. The final results will be the best output of
#' n_init consecutive runs in terms of cost.
#' init : {'Huang', 'Cao', 'random' or a list of ndarrays}
#' Method for initialization:
#' 'Huang': Method in Huang [1997, 1998]
#' 'Cao': Method in Cao et al. [2009]
#' 'random': choose 'n_clusters' observations (rows) at random from
#' data for the initial centroids.
#' If a list of ndarrays is passed, it should be of length 2, with
#' shapes (n_clusters, n_features) for numerical and categorical
#' data respectively. These are the initial centroids.
#' verbose : integer, optional
#' Verbosity mode.
#' Attributes
#' ----------
#' cluster_centroids_ : array, [n_clusters, n_features]
#' Categories of cluster centroids
#' labels_ :
#' Labels of each point
#' cost_ : float
#' Clustering cost, defined as the sum distance of all points to
#' their respective cluster centroids.
#' Notes
#' -----
#' See:
#' Huang, Z.: Extensions to the k-modes algorithm for clustering large
#' data sets with categorical values, Data Mining and Knowledge
#' Discovery 2(3), 1998.
#' """
#'
#' def __init__(self, n_clusters=8, gamma=None, init='Huang', n_init=10,
#' max_iter=100, verbose=0):
#'
#' super(KPrototypes, self).__init__(n_clusters, init, n_init, max_iter, verbose)
#'
#' self.gamma = gamma
#'
#' def fit(self, X, y=None, categorical=None):
#' """Compute k-prototypes clustering.
#' Parameters
#' ----------
#' X : array-like, shape=[n_samples, n_features]
#' categorical : Index of columns that contain categorical data
#' """
#'
#' # If self.gamma is None, gamma will be automatically determined from
#' # the data. The function below returns its value.
#' self.cluster_centroids_, self.labels_, self.cost_, self.n_iter_, \
#' self.gamma, self.enc_map_ = k_prototypes(X,
#' categorical,
#' self.n_clusters,
#' self.gamma,
#' self.init,
#' self.n_init,
#' self.max_iter,
#' self.verbose)
#' return self
#'
#' def predict(self, X, categorical=None):
#' """Predict the closest cluster each sample in X belongs to.
#' Parameters
#' ----------
#' X : array-like, shape = [n_samples, n_features]
#' New data to predict.
#' categorical : Index of columns that contain categorical data
#' Returns
#' -------
#' labels : array, shape [n_samples,]
#' Index of the cluster each sample belongs to.
#' """
#' assert hasattr(self, 'cluster_centroids_'), "Model not yet fitted."
#'
#' Xnum, Xcat = _split_num_cat(X, categorical)
#' Xnum, Xcat = check_array(Xnum), check_array(Xcat, dtype=None)
#' Xcat, _ = encode_features(Xcat, enc_map=self.enc_map_)
#' return _labels_cost(Xnum, Xcat, self.cluster_centroids_, self.gamma)[0]
#'
#' mu,sigma=0,0.3 #均值与标准差
#' rarray1=numpy.random.normal(mu,sigma,10)
#' mu,sigma=1,0.3 #均值与标准差
#' rarray2=numpy.random.normal(mu,sigma,10)
不同于k-means的划分聚类方法,k-medoid聚类算法每个簇用接近于簇中心的一个对象表示,通过反复迭代运算,当准则函数收敛时,得到最终聚类结果。k-medoid在有孤立点的情况下稳健性较好,该方法对数据的平移与正交变换结果不变
下面介绍其中一种:围绕中心点的划分(partitioning around medioids,PAM),该算法由Kaufman与Rousseeuw开发。除此之外还有ClARA算法,基于随机搜索的聚类CLARANS等等。
# # k-Medoids #
# library(cluster)
#
# fit_pam=pam(countries[,-1],3)
# print(fit_pam)
# head(fit_pam$data)
#
# fit_pam1=pam(countries[,-1],3,keep.data=FALSE)
# fit_pam1$data
# fit_pam2=pam(countries[,-1],3,cluster.only=TRUE)
# print(fit_pam2)
#
# which(fit_km$cluster!=fit_pam$cluster)
#
# plot(countries[,-1],pch=(fit_pam$cluster-1))
# c1=which(rownames(countries)==rownames(fit_pam$medoids)[1])
# c2=which(rownames(countries)==rownames(fit_pam$medoids)[2])
# c3=which(rownames(countries)==rownames(fit_pam$medoids)[3])
# for(i in 1:3)
# { var=c(c1,c2,c3)
# points(countries[var[i],-1],pch=fit_pam$cluster[var[i]]+14) }
# legend(fit_pam$medoids[1,1],fit_pam$medoids[1,2],paste("Center_1:",rownames(fit_pam$medoids)[1]),bty="n",xjust=0.5,yjust=0,cex=0.8)
# legend(fit_pam$medoids[2,1]-1.2,fit_pam$medoids[2,2],paste("Center_2:",rownames(fit_pam$medoids)[2]),bty="n",xjust=0,yjust=0.5,cex=0.8)
# legend(fit_pam$medoids[3,1],fit_pam$medoids[3,2]+3.5,paste("Center_3:",rownames(fit_pam$medoids)[3]),bty="n",xjust=0.5,yjust=0,cex=0.8)
# points(countries[c(21,23,33),-1],pch=12)
# legend(countries$birth[21],countries$death[21],"MONGOLIA",bty="n",xjust=0.5,yjust=0,cex=0.8)
# legend(countries$birth[23]-1.2,countries$death[23],"SYRIA",bty="n",xjust=0,yjust=0.5,cex=0.8)
# legend(countries$birth[33]-1.2,countries$death[33],"PANAMA",bty="n",xjust=0,yjust=0.5,cex=0.8)
# result=matrix(0,66,2)
# for(k in 2:67)
# {
# fit_pam=pam(countries[,-1],k)
# result[k-1,]=fit_pam$objective
# }
# plot(2:67,result[,1],type="l",main="Choosing the Optimal Number of Cluster",
# xlab="number of cluster: 2 to 67",ylab="betweenss/totss")
# points(2:67,result[,2],type="l",col="red")
# points(10,result[10],pch=16)
# legend(10,result[10],paste("(10,",sprintf("%.1f%%",result[10]*100),")",sep=""),bty="n",xjust=0.3,cex=0.8)基于密度的聚类方法将簇看作是空间数据中被低密度区域(代表噪声)分割开的稠密对象区域,通过数据 密度来发现任意形状的类簇(Han 2007,孙吉贵,2008)
# # DBSCAN #
# library(fpc)
# # dbscan(data,eps,MinPts=5,scale=FALSE,method=c("hybrid","raw","dist"),
# # seeds=TRUE,showplot=FALSE,countmode=NULL)
#
# ds1=dbscan(countries[,-1],eps=1,MinPts=5)
# ds2=dbscan(countries[,-1],eps=4,MinPts=5)
# ds3=dbscan(countries[,-1],eps=4,MinPts=2)
# ds4=dbscan(countries[,-1],eps=8,MinPts=2)
# par(mfcol=c(2,2))
# plot(ds1,countries[,-1],main="1: MinPts=5 eps=1")
# plot(ds3,countries[,-1],main="3: MinPts=2 eps=4")
# plot(ds2,countries[,-1],main="2: MinPts=5 eps=4")
# plot(ds4,countries[,-1],main="4: MinPts=2 eps=8")
#
# d=dist(countries[,-1])
# max(d);min(d)
# library(ggplot2)
# interval=cut_interval(d,30)
# table(interval)
# which.max(table(interval))
#
# for(i in 3:5)
# { for(j in 1:10)
# { ds=dbscan(countries[,-1],eps=i,MinPts=j)
# print(ds)
# }
# }
#
# ds5=dbscan(countries[,-1],eps=3,MinPts=2)
# ds6=dbscan(countries[,-1],eps=4,MinPts=5)
# ds7=dbscan(countries[,-1],eps=5,MinPts=9)
# par(mfcol=c(1,3))
# plot(ds5,countries[,-1],main="1: MinPts=2 eps=3")
# plot(ds6,countries[,-1],main="3: MinPts=5 eps=4")
# plot(ds7,countries[,-1],main="2: MinPts=9 eps=5")期望最大化算法(EM算法)的思路巧妙,在使用该算法进行聚类时,他将数据看作是一个含有隐 性变量的概率模型,并以实现模型最优化,即获取与数据本身性质最契合的聚类方式为目的,通过‘反复估计’模型参数找出最优解,同时给出最优类别数k。而反复估计的过程即为EM算法的精华所在,分为E(Expectation)步和M(Maximization)步交替进行,在我们学习的高等数理统计中都有详细的介绍。
# # EM #
# library(mclust)
#
# fit_EM=Mclust(countries[,-1])
# summary(fit_EM)
# summary(fit_EM,parameters=TRUE)
# plot(fit_EM)
#
# countries_BIC=mclustBIC(countries[,-1])
# countries_BICsum=summary(countries_BIC,data=countries[,-1])
# countries_BICsum
#
# countries_BIC
# plot(countries_BIC,G=1:7,col="black")
#
# names(countries_BICsum)
# mclust2Dplot(countries[,-1], classification=countries_BICsum$classification,parameters=countries_BICsum$parameters,col="black")
#
# countries_Dens=densityMclust(countries[,-1])
# plot(countries_Dens,countries[,-1],col="grey",nlevels=55)
# plot(countries_Dens,type = "persp",col = grey(0.8))当然还有其他的聚类方法,在机器学习(文本聚类)中,要对聚类方法有透彻的理解 并确定哪种方法更适合我们所处理的项目数据,合理的调整模型中的参数,最终输出结果 以可视化的方式结合业务合理解释自己的分群。一个完整的以无监督学习方式的分析报告的初版 才算完成。不要高兴的太早,我说的是初版,迎接我们的将是与业务部门的对接与项目无休止的修改,最终项目上线。
只有不断的学习和探究,才有可能在人生的道路上有一点点的进步,我还在路上。。。。。。
这一部分我不打算细说(只介绍一个模型:条件随机场CRF,情感分析中),文本分类中我们仍然会用到一些经典的机器学习模型 ,最终的文本都会转化成数据,而文本的分类最终还是对数据的分类,ML中经典的分类模型:例如 Logistic回归(LR 二分类),softmax回归(多分类),最大熵模型,决策树,KNN,朴素贝叶斯(NB),SVM,神经网络(ANN),一些集成学习:Bagging,Boosting,RF,GBDT,XGB等等,还有一些深度学习模型:自编码器,深层玻尔茨曼机,和积网络,卷积网络,深层信念网络,深层堆叠网络,长短时记忆网络等等的一些深层的神经网络,Bayes网络。
如果你只熟悉R,没关系这些你都可以尝试,只不过当数据量较大时你需要一些并行计算或分布式文件存储的技术(如Rspark,Rhadoop),或者你可以用R操作你的数据库(上一次培训中已经介绍),或者安装R server,可以解决一些大数据的问题。从模型的应用上来说Java可以调用我们用R写好的算法,完全可以嵌入系统(我担心会慢)。
如果你熟悉Python或Java那就更好不过了,在以前的项目分析和工作中,我发现在数据挖掘算法的训练和构建上Python要比R更方便,用起来更舒服,因为Python中也有很多机器学习和深度学习的库供我们使用,你也可以随意设计自己的算法。当然在可视化和前端展示上,我更推荐大家使用R(Python中的matplotlab,ggplot等库做静态可视化也不错),因为htmlwidgets包使得很多世界上开源的js库(eg:echarts,d3,threejs,leaflet……)都可以嫁接到R,甚至你可以基于htmlweidgets开发自己的js可视化的R包,此外shiny的诞生,使得我们在熟悉H5,CSS,及javascript等前端开发语言的基础上,可以快速的搭建自己的分析报告,shinyjs可以让我们在shiny中书写javascript,实现更复杂的前端任务。所以:我推荐用R做模型的尝试及可视化和前端展示,确定模型后用Python训练模型,调整参数。
条件随机场模型是Lafferty于2001年,在最大熵模型和隐马尔科夫模型的基础上,提出的一种判别式概率无向图学习模型, 是一种用于标注和切分有序数据的条件概率模型。CRF最早是针对序列数据分析提出的,现已成功应用于自然语言处理(Natural Language Processing,NLP) 、生物信息学、机器视觉及网络智能等领域。
从概率图模型的角度看,条件随机场(Conditional Random Field,CRF)是在给定一组输入随机变量或观测变量X的条件下,另一组输出随机变量或目标变量Y的条件 概率分布模型,其特点是假定目标变量集构成马尔科夫随机场(概率无向图模型,又称为马尔科夫网络(MN),马尔科夫随机场(MRF):对应一个无向图。这个无向图上的每一个节点对应一个随机变量,节点之间的边表示节点对应的随机变量之间有概率依赖关系。因此,MRF的结构本质上反应了我们的先验知识——哪些变量之间有依赖关系需要考虑,而哪些可以忽略。 )。所以条件随机场实际上可以看做是一个通过 观测变量集X和目标变量集Y定义的无向图,或一个在给定X时,表达Y的概率分布结构的马尔科夫网络,但与其把他看做是联合概率分布P(X,Y)的刻画,还不如 把他看做是是对条件概率P(Y|X)的刻画。P(Y|X)称为条件随机场,
现在,如果给定的MRF中每个随机变量下面还有观察值,我们要确定的是给定观察集合下,这个MRF的分布,也就是条件分布,那么这个MRF就称为CRF。它的条件分布形式完全类似于MRF的分布形式,只不过多了一个观察集合x。
最通用角度来看,CRF本质上是给定了观察值 (observations)集合的MRF
如果表达P(Y,X)的马尔科夫随机场对任意节点Y\(\in\)Y,满足下面的马尔科夫性质, \[P(Y|X,Y-{Y})=P(Y|X,Nb(Y))\] 根据HC定理,条件随机场的条件概率分布P(Y|X)可以通过一组极大团\(D_iUX\)的因子\(\psi_i(D_i)\),(\(i=1,...,l\))表达如下: \[P(Y|X)=\frac{1}{Z(X)}\hat{P}(Y,X)\] \[\hat{P}(Y,X)=\prod_{i=1}^{l}\psi_i(D_i)\] \[Z(X)=\sum_{Y\in val(Y)}\hat{P}(Y,X)\] 设\(X=\{X_i,X_2,...,X_n\}\)和\(Y=\{Y_1,...,Y_n\}\),条件概率分布P(Y|X)称为线性链条件随机场,如果满足下面的线性条件马尔科夫性质: \[P(Y_i|X,Y_1,...,Y_{i-1},Y_{i+1},...,Y_n)=P(Y_i|X,Y_{i-1},Y_{i+1})\]
线性链条件随机场中,所有的极大团是\(Y_i-Y_{Ii+1}\)和\(Y_{i}-X_i\),因此根据HC定理,其概率分布形式具有如下形式: \[P(Y|X)=\frac{1}{Z(X)}\hat{P}(Y,X)\] \[\hat{P}(Y,X)=\prod_{i=1}^{n-1}\psi_i(Y_i,Y_{i+1})\prod_{i=1}^{n}\psi_i(Y_i,X_i)\] \[Z(X)=\sum_{Y \in val(Y)} \hat{P}(Y,X)\] 此外,线性链条随机场还可以表示为对数线性模型的参数化形式,在实际应用中更为普遍。
上边的具体推导你可能觉得有些晦涩,学习过Bayesian统计和概率图模型的应该会很容易,推导一边完全清晰,下面给一个直观一点的定义(这才是人类的语言)
# require(tmcn.crfpp)
# TestFilePath<-system.file("tests",package="tmcn.crfpp")
# WorkPath <- tempdir()
# # Learn
# TempletFile <- file.path(TestFilePath,"testdata", "chunking_template")
# TrainingFile <- file.path(TestFilePath,"testdata", "chunking_train")
# ModelFile1 <- file.path(WorkPath, "output", "model1")
# res1 <- crflearn(TempletFile, TrainingFile, ModelFile1)
# # Test
# KeyFile <- file.path(TestFilePath, "testdata","chunking_key")
# ResultFile1 <- file.path(WorkPath, "output", "result1")
# test1 <- crftest(res1$model_file, KeyFile, ResultFile1)# #----------------------CRF-packages------------------------------------
# ##Markov Random Field
#
# library(CRF)
# #set the parametters for Markov chain model:
# #The markov chain consists of 10 nodes and there are 2 states for each node. The prior probability is prior.prob and the transition probability is trans.prob
# n.nodes<-10
# n.states<-2
# pror.prob<-c(0.8,0.3)
# trans.prob<-matrix(0,nrow=2,ncol=2)
# trans.prob[1,]<-c(0.95,0.05)
# trans.prob[2,]<-c(0.05,0.95)
#
# #Then we constructed the adjacent matrix of chain:
#
# adj<-matrix(0,n.nodes,n.nodes)
# for (i in 1:(n.nodes-1))
# {
# adj[i,i+1]<-1
# }
#
# # Note that the adjacent matrix will be automatically symmetrized when used to build the CRF object,
# # therefore only the upper (or lower) triangular matrix is need here.
#
# #Now we can build the CRF object for Markov chain model:
#
# mc <- make.crf(adj, n.states)
# #set the parameters:
#
# mc$node.pot[1,] <- prior.prob
# for (i in 1:mc$n.edges)
# {
# mc$edge.pot[[i]] <- trans.prob
# }
#
# ##generate samples
# # We generated 10000 samples from the Markov chain model and displayed the first 10 samples:
# mc.samples <- sample.chain(mc, 10000)
# mc.samples[1:10, ]
#
# ##learn Markov random field model from MC data
#
# # In order to learn Markov random field model from generated data, we first built another CRF object:
#
# mrf.new <- make.crf(adj, n.states)
# #and created the paramter structure:
# mrf.new <- make.features(mrf.new)
# mrf.new <- make.par(mrf.new, 4)
#
# # We only need 4 paramters in the MRF model, one for prior probability and three for transition probability,
# # since the probabilities are summed to one.
#
# mrf.new$node.par[1,1,1] <- 1
# for (i in 1:mrf.new$n.edges)
# {
# mrf.new$edge.par[[i]][1,1,1] <- 2
# mrf.new$edge.par[[i]][1,2,1] <- 3
# mrf.new$edge.par[[i]][2,1,1] <- 4
# }
#
# #Then we trained the model using train.mrf function:
# mrf.new <- train.mrf(mrf.new, mc.samples)
# #After training, we can check the parameter values:
# mrf.new$par
#
# #We normalized the potentials in MRF to make it more like probability:
# mrf.new$node.pot <- mrf.new$node.pot / rowSums(mrf.new$node.pot)
# mrf.new$edge.pot[[1]] <- mrf.new$edge.pot[[1]] / rowSums(mrf.new$edge.pot[[1]])
# #Now we can check the learned prior probability
# mrf.new$node.pot[1,]
# #and transition probability
# mrf.new$edge.pot[[1]]
#
#
# #----learned a conditional random field (CRF) model from the HMM data.
# # Generate samples
# # Suppose that the Markov chain can not be directly observed. There are 4 observation states and the
# # observation probability (emmision probability) is given as follows:
# emmis.prob <- matrix(0, nrow=2, ncol=4)
# emmis.prob[1,] <- c(0.59, 0.25, 0.15, 0.01)
# emmis.prob[2,] <- c(0.01, 0.15, 0.25, 0.59)
# emmis.prob
# #We simulated the observation data from Markov chain samples:
# hmm.samples <- mc.samples
# hmm.samples[mc.samples == 1] <- sample.int(4, sum(mc.samples == 1), replace=T, prob=emmis.prob[1,])
# hmm.samples[mc.samples == 2] <- sample.int(4, sum(mc.samples == 2), replace=T, prob=emmis.prob[2,])
# hmm.samples[1:10,]
#
# # Learn conditional random field model from HMM data
# # Now we try to learn a CRF model from HMM data. We first built another CRF object:
# crf.new <- make.crf(adj, n.states)
# #and created the paramter structure:
# crf.new <- make.features(crf.new, 5, 1)
# crf.new <- make.par(crf.new, 8)
# # The major difference between CRF and MRF is that we have 5 node features now, instead of 1 constant
# # feature in MRF model. The first node feature is the constant feature as in MRF model, and the other 4 node
# # features correspond to observation states respectively. The number of edge feature is still one. We now need
# # eight paramters, one for prior probability, three for transition probability, and four for emmision probability.
#
# crf.new$node.par[1,1,1] <- 1
# for (i in 1:crf.new$n.edges)
# {
# crf.new$edge.par[[i]][1,1,] <- 2
# crf.new$edge.par[[i]][1,2,] <- 3
# crf.new$edge.par[[i]][2,1,] <- 4
# }
# crf.new$node.par[,1,2] <- 5
# crf.new$node.par[,1,3] <- 6
# crf.new$node.par[,1,4] <- 7
# crf.new$node.par[,1,5] <- 8
#
# #We prepared the node features and the edge features, which are need for training:
# hmm.nf <- lapply(1:dim(hmm.samples)[1], function(i) matrix(1, crf.new$n.nf, crf.new$n.nodes))
# for (i in 1:dim(hmm.samples)[1])
# {
# hmm.nf[[i]][2, hmm.samples[i,] != 1] <- 0
# hmm.nf[[i]][3, hmm.samples[i,] != 2] <- 0
# hmm.nf[[i]][4, hmm.samples[i,] != 3] <- 0
# hmm.nf[[i]][5, hmm.samples[i,] != 4] <- 0
# }
# hmm.ef <- lapply(1:dim(hmm.samples)[1], function(i) matrix(1, crf.new$n.ef, crf.new$n.edges))
# #Then we trained the model using train.crf function:
# crf.new <- train.crf(crf.new, mc.samples, hmm.nf, hmm.ef)
# #After training, we can check the parameter values:
# crf.new$par
#
# #With trained CRF model, we can infer the hidden states given the observations:
# hmm.infer <- matrix(0, nrow=dim(hmm.samples)[1], ncol=dim(hmm.samples)[2])
# for (i in 1:dim(hmm.samples)[1])
# {
# crf.new <- crf.update(crf.new, hmm.nf[[i]], hmm.ef[[i]])
# hmm.infer[i,] <- decode.chain(crf.new)
# }
# #The inferred result was compared with the true hidden states:
# sum(hmm.infer != mc.samples)
#
# # Use other inference methods in the training
# # The default inference method used in the train.mrf and train.crf functions is infer.chain, which can
# # only handle chain-structured graphs. We can provide the preferred inference method when calling the
# # training functions. For example, use the loopy brief propagation algorithm:
# crf.new <- train.crf(crf.new, mc.samples, hmm.nf, hmm.ef, infer.method = infer.lbp)
# # In a more complicated way, we can redefine the functions for calculating the negative log-likelihood, i.e., the
# # functions mrf.nll and crf.nll, respectively.
# D. M. Blei在2003年(准确地说应该是2002年)提出的LDA(Latent Dirichlet Allocation)模型(翻译成中文就是——潜在狄利克雷分配模型)让主题模型火了起来, 今年3月份我居然还发现了一个专门的lda的R软件包及可视化主题模型的LDAvis包,主题模型是一种语言模型,是对自然语言进行建模,这个在信息检索中很有用。
LDA主题模型涉及到贝叶斯理论、Dirichlet分布、多项分布、图模型、变分推断、EM算法、Gibbs抽样等知识,不是很好懂。主题模型其实也不只是LDA 了,LDA之前也有主题模型,它是之前的一个突破,它之后也有很多对它进行改进的主题模型。需要注意的是,LDA也是有名的Linear Discriminant Analysis(线性判别分析)的缩写。
LDA是一种非监督机器学习技术,可以用来识别大规模文档集(document collection)或语料库(corpus)中潜藏的主题信息。它采用了词袋(bag of words)的方法,这种方法将每一篇文档视为一个词频向量,从而将文本信息转化为了易于建模的数字信息。但是词袋方法没有考虑词与词之间的顺序,这简化了问题的复杂性,同时也为模型的改进提供了契机。每一篇文档代表了一些主题所构成的一个概率分布,而每一个主题又代表了很多单词所构成的一个概率分布。由于 Dirichlet分布随机向量各分量间的弱相关性(之所以还有点“相关”,是因为各分量之和必须为1),使得我们假想的潜在主题之间也几乎是不相关的,这与很多实际问题并不相符,从而造成了LDA的又一个遗留问题。
对于语料库中的每篇文档,LDA定义了如下生成过程(generative process):
对每一篇文档,从主题分布中抽取一个主题;
从上述被抽到的主题所对应的单词分布中抽取一个单词;
重复上述过程直至遍历文档中的每一个单词。
更形式化一点说,语料库中的每一篇文档与\(T\)(通过反复试验等方法事先给定)个主题的 一个多项分布相对应,将多项分布记为\(\theta\)(主题的分布)。每个主题又与词汇表(vocabulary)中的\(V\)个单词的一个多项分布(词汇的多项分布)相对应,将这个多项分布记为\(\phi\)。 上述词汇表是由语料库中所有文档中的所有互异单词组成,但实际建模的时候要踢除一些停用词, 还要进行一些词干化(针对于英文)处理等。\(\theta\)和\(\phi\)分别有一个带有超参数\(\alpha\)和 \(\beta\)的Dirichlet先验分布。对于一篇文档\(d\)中的每一个单词,我们从该文档所对应的多项式分布\(\theta\)中抽取一个主题\(z\),然后我们再从主题\(z\)所对应的多项分布\(\phi\)中抽取一个单词\(\omega\)。将这个过程重复\(N_d\)次,就产生了文档\(d\),这里的\(N_d\)是文档\(d\)的单词总数。这个生成过程可以用如下的图模型表示:
这个图模型表示法也称作“盘子表示法”(plate notation)。图中的阴影圆圈表示可观测变量(observed variable),非阴影圆圈表示潜在变量(latent variable),箭头表示两变量间的条件依赖性(conditional dependency),方框表示重复抽样,重复次数在方框的右下角。
该模型有两个参数需要推断(infer):一个是“文档-主题”分布\(\theta\),另外是\(T\)个“主题-单词”分布\(\phi\)。通过学习(learn)这两个参数,我们可以知道文档作者感兴趣的主题,以及每篇文档所涵盖的主题比例等。推断方法主要有LDA模型作者提出的变分-EM算法,还有现在常用的Gibbs抽样法。
LDA模型现在已经成为了主题建模中的一个标准。如前所述,LDA模型自从诞生之后有了蓬勃的扩展,特别是在社会网络和社会媒体研究领域最为常见。
R中lda包就可以做LDA模型,LDAvis做LDA可视化,详细的用法可下载 lda及LDAvis的说明文档,有详细介绍。
#---------------lda演示-------------------
# lda.collapsed.gibbs.sampler(documents, K, vocab, num.iterations, alpha,
# eta, initial = NULL, burnin = NULL, compute.log.likelihood = FALSE,
# trace = 0L, freeze.topics = FALSE)
# slda.em(documents, K, vocab, num.e.iterations, num.m.iterations, alpha,
# eta, annotations, params, variance, logistic = FALSE, lambda = 10,
# regularise = FALSE, method = "sLDA", trace = 0L, MaxNWts=3000)
# mmsb.collapsed.gibbs.sampler(network, K, num.iterations, alpha,
# beta.prior, initial = NULL, burnin = NULL, trace = 0L)
# lda.cvb0(documents, K, vocab, num.iterations, alpha, eta, trace = 0L)
library(lda)
## Not run: demo(lda)
demo(slda)##
##
## demo(slda)
## ---- ~~~~
##
## > set.seed(8675309)
##
## > ## Use the political blogs data set.
## > data(poliblog.documents)
##
## > data(poliblog.vocab)
##
## > data(poliblog.ratings)
##
## > num.topics <- 10
##
## > ## Initialize the params
## > params <- sample(c(-1, 1), num.topics, replace=TRUE)
##
## > result <- slda.em(documents=poliblog.documents,
## + K=num.topics,
## + vocab=poliblog.vocab,
## + num.e.iterations=10,
## + num.m.iterations=4,
## + alpha=1.0, eta=0.1,
## + poliblog.ratings / 100,
## + params,
## + variance=0.25,
## + lambda=1.0,
## + logistic=FALSE,
## + method="sLDA")
##
## > ## Make a pretty picture.
## > require("ggplot2")
## Loading required package: ggplot2
##
## > Topics <- apply(top.topic.words(result$topics, 5, by.score=TRUE),
## + 2, paste, collapse=" ")
##
## > coefs <- data.frame(coef(summary(result$model)))
##
## > theme_set(theme_bw())
##
## > coefs <- cbind(coefs, Topics=factor(Topics, Topics[order(coefs$Estimate)]))
##
## > coefs <- coefs[order(coefs$Estimate),]
##
## > qplot(Topics, Estimate, colour=Estimate, size=abs(t.value), data=coefs) +
## + geom_errorbar(width=0.5, aes(ymin=Estimate-Std..Error,
## + ymax=Estimate+Std..Error)) + coord_flip()
##
## > predictions <- slda.predict(poliblog.documents,
## + result$topics,
## + result$model,
## + alpha = 1.0,
## + eta=0.1)
##
## > qplot(predictions,
## + fill=factor(poliblog.ratings),
## + xlab = "predicted rating",
## + ylab = "density",
## + alpha=I(0.5),
## + geom="density") +
## + geom_vline(aes(xintercept=0)) +
## + theme(legend.position = "none")
##
## > predicted.docsums <- slda.predict.docsums(poliblog.documents,
## + result$topics,
## + alpha = 1.0,
## + eta=0.1)
##
## > predicted.proportions <- t(predicted.docsums) / colSums(predicted.docsums)
##
## > qplot(`Topic 1`, `Topic 2`,
## + data = structure(data.frame(predicted.proportions),
## + names = paste("Topic", 1:10)),
## + size = `Topic 3`)
## Not run: demo(mmsb)#----------------LDAvis演示------------------
library(LDAvis)
library(LDAvisData)
data(Jeopardy, package="LDAvisData")
json <- with(Jeopardy,
createJSON(phi, theta, doc.length, vocab, term.frequency))
serVis(json) ## Loading required namespace: servr
# Check out Topic 22 (bodies of water!)
# If you have a GitHub account, you can even publish as a gist
# which allows you to easily share with others!
#serVis(json, as.gist = TRUE)Python中做LDA
'''
需要安装Microsoft Visual C++ 9.0
import sys
reload(sys)
sys.setdefaultencoding("UTF-8")
import numpy as np
import lda
import lda.datasets
import jieba
import codecs
class LDA_v20161130():
def __init__(self, topics=2):
self.n_topic = topics
self.corpus = None
self.vocab = None
self.ppCountMatrix = None
self.stop_words = [u',', u'。', u'、', u'(', u')', u'·', u'!', u' ', u':', u'“', u'”', u'\n']
self.model = None
def loadCorpusFromFile(self, fn):
# 中文分词
f = open(fn, 'r')
text = f.readlines()
text = r' '.join(text)
seg_generator = jieba.cut(text)
seg_list = [i for i in seg_generator if i not in self.stop_words]
seg_list = r' '.join(seg_list)
# 切割统计所有出现的词纳入词典
seglist = seg_list.split(" ")
self.vocab = []
for word in seglist:
if (word != u' ' and word not in self.vocab):
self.vocab.append(word)
CountMatrix = []
f.seek(0, 0)
# 统计每个文档中出现的词频
for line in f:
# 置零
count = np.zeros(len(self.vocab),dtype=np.int)
text = line.strip()
# 但还是要先分词
seg_generator = jieba.cut(text)
seg_list = [i for i in seg_generator if i not in self.stop_words]
seg_list = r' '.join(seg_list)
seglist = seg_list.split(" ")
# 查询词典中的词出现的词频
for word in seglist:
if word in self.vocab:
count[self.vocab.index(word)] += 1
CountMatrix.append(count)
f.close()
#self.ppCountMatrix = (len(CountMatrix), len(self.vocab))
self.ppCountMatrix = np.array(CountMatrix)
print "load corpus from %s success!"%fn
def setStopWords(self, word_list):
self.stop_words = word_list
def fitModel(self, n_iter = 1500, _alpha = 0.1, _eta = 0.01):
self.model = lda.LDA(n_topics=self.n_topic, n_iter=n_iter, alpha=_alpha, eta= _eta, random_state= 1)
self.model.fit(self.ppCountMatrix)
def printTopic_Word(self, n_top_word = 8):
for i, topic_dist in enumerate(self.model.topic_word_):
topic_words = np.array(self.vocab)[np.argsort(topic_dist)][:-(n_top_word + 1):-1]
print "Topic:",i,"\t",
for word in topic_words:
print word,
print
def printDoc_Topic(self):
for i in range(len(self.ppCountMatrix)):
print ("Doc %d:((top topic:%s) topic distribution:%s)"%(i, self.model.doc_topic_[i].argmax(),self.model.doc_topic_[i]))
def printVocabulary(self):
print "vocabulary:"
for word in self.vocab:
print word,
print
def saveVocabulary(self, fn):
f = codecs.open(fn, 'w', 'utf-8')
for word in self.vocab:
f.write("%s\n"%word)
f.close()
def saveTopic_Words(self, fn, n_top_word = -1):
if n_top_word==-1:
n_top_word = len(self.vocab)
f = codecs.open(fn, 'w', 'utf-8')
for i, topic_dist in enumerate(self.model.topic_word_):
topic_words = np.array(self.vocab)[np.argsort(topic_dist)][:-(n_top_word + 1):-1]
f.write( "Topic:%d\t"%i)
for word in topic_words:
f.write("%s "%word)
f.write("\n")
f.close()
def saveDoc_Topic(self, fn):
f = codecs.open(fn, 'w', 'utf-8')
for i in range(len(self.ppCountMatrix)):
f.write("Doc %d:((top topic:%s) topic distribution:%s)\n" % (i, self.model.doc_topic_[i].argmax(), self.model.doc_topic_[i]))
f.close()
'''word2vec 是 Google 于 2013 年开源推出的一个用于获取 word vector 的工具包,它简单、高效,因此引起了很多人的关注。由于 word2vec 的作者 Tomas Mikolov 在两篇相关的论文 中并没有谈及太多算法细节,因而在一定程度上增加了这个工具包的神秘感。word2vec用到的知识点主要有:sigmod函数,Baysain公式,huffman编码等,前两个我们都相当熟悉了,介绍一下huffman编码。
Huffman树:在计算科学中,树是一种重要的非线性数据结构,他是数据元素(在树中称为结点)按分支关系组织起来的结构。若干棵互不相交的树所构成的集合称为森林,下面给出几个与树相关的常用概念。
路径和路径长度:在一个树中,从一个结点往下可以达到的孩子和孙子结点之间的通路,称为路径,通路中分支的数目称为路径长度,若规定根节点的层号为1,则从根节点到第L层结点的路径长度为L-1
结点的权和带权路径长度:若为树中结点赋予一个具有某种含义(非负)数值,这个数值称为该节点的权,结点的代全路径长度是指,从根结点到该结点之间的路径长度与该结点的权的乘积。
树的带权路径长度:树的带权路径长度表示所有叶子结点的代全路径长度之和
二叉树是每个结点最多有两个子树的有序树,两个字数通常被称为“左子树”和“右子树”,定义中的有序是指两个子树有左右之分,顺序不能颠倒。
给定n个权值作为n个叶子结点,构造一颗二叉树,若它的代全路径长度达到最小,则称这样的二叉树为最优二叉树,也成Huffman树。
Huffman树的构造:
例子:
Huffman编码:
# R语言中tmcn.word2vec,写于2014年9月21日。
#
# 后期也没有维护
#
# 该包有两个函数,第一个函数是word2vec,第二个函数计算单词之间cos距离。
#
#
#
# 1、word2vec介绍
#
# 其中train_file代表输入训练语料库,以及output_file输出位置。
#
#
#
#
# word2vec <- function (train_file, output_file,
# binary=1, # output format, 1-binary, 0-txt
# cbow=0, # skip-gram (0) or continuous bag of words (1)
# num_threads = 1, # num of workers
# num_features = 300, # word vector dimensionality
# window = 10, # context / window size
# min_count = 40, # minimum word count
# sample = 1e-3, # downsampling of frequent words
# classes = 0
# )
#
# # The parameters:
# # binary - output format of the model;
# #
# # cbow - which algorithm to use for training or skip-gram bag of words (cbow).
# # Skip-gram is slower but produces a better result on the rare words;
# #
# # num_threads - Processor number of threads involved in the construction of the model;
# #
# # num_features - the dimension of words (or a vector for each word), it is recommended from tens to hundreds;
# #
# # window - as many words from the context of the training algorithm should be taken into account;
# #
# # min_count - limits the size of a boost word dictionary.
# # Words that are not found in the text more than the specified number are ignored.
# # Recommended value - from ten to one hundred;
# #
# # sample - the lower limit of the frequency of occurrence of words in the text,
# # it is recommended from .00001 to .01.
#
# 以下关于参数的解释是来源于linux环境模拟器,cygwin中操作,用Java来调用的,发现R中这个包也跟这个函数参数,大同小异,而且解释很清楚,所以非常感谢作者的细心翻译[1]。
#
# 参数解释:
# -train_file 训练数据
# -output_file 结果输入文件,即每个词的向量
# -cbow 是否使用cbow模型,0表示使用skip-gram模型,1表示使用cbow模型,默认情况下是skip-gram模型,cbow模型快一些,skip-gram模型效果好一些
# -num_features 表示输出的词向量维数
# -window 为训练的窗口大小,8表示每个词考虑前8个词与后8个词(实际代码中还有一个随机选窗口的过程,窗口大小<=5)
# -sample 表示 采样的阈值,如果一个词在训练样本中出现的频率越大,那么就越会被采样
# -binary 表示输出的结果文件是否采用二进制存储,0表示不使用(即普通的文本存储,可以打开查看),1表示使用,即vectors.bin的存储类型
# -------------------------------------
# 除了上面所讲的参数,还有:
# -alpha 表示 学习速率
# -min-count 表示设置最低频率,默认为5,如果一个词语在文档中出现的次数小于该阈值,那么该词就会被舍弃
# -classes 表示词聚类簇的个数,从相关源码中可以得出该聚类是采用k-means
#
#
#
# 模型训练完成之后,得到了.bin这个词向量文件,文件的存储类型由binary参数觉得,如果为0,便可以直接用编辑器打开,进行查看.其中word2vec中提供了distance求词的cosine相似度,并排序。也可以在训练时,设置-classes参数来指定聚类的簇个数,使用kmeans进行聚类。
#
#
# 2、disttance函数
#
# 由于word2vec计算的是余弦值,距离范围为0-1之间,值越大代表这两个词关联度越高,所以越排在上面的词与输入的词越紧密[2]。
#
# distance(file_name, word)
# file_name;Path of the modle file.
# word;The search word.
# 输出是一个list,然后可以得到cos距离。
# 一个在线测试的网站,貌似是一位清华教授做的:http://cikuapi.com/index.php[2]
# 整个包就这样结束了。。
#Python中有gensim模块可以调用word2vec
# NLP WordEmbedding的概念和实现
'''
背景
如何表示词语所包含的语义?
苹果?水果?Iphone?
苹果、梨子,这两个词相关吗?
语言的表示
符号主义:Bags-of-word,维度高、过于稀疏、缺乏语义、模型简单
分布式表示:Word Embedding,维度低、更为稠密、包含语义、训练复杂(word2vec)
Word Embedding
核心思想:语义相关的词语,具有相似的上下文环境,例如, 苹果和梨子 (并不是近义词)
所做的事情:将每个词语训练成,词向量
实践
基于gensim包和中文维基语料
gensim:http://radimrehurek.com/gensim/models/word2vec.html
中文维基分词语料:链接 https://pan.baidu.com/s/1qXKIPp6 密码 kade
'''
'''
# 加载包
from gensim.models import Word2Vec
from gensim.models.word2vec import LineSentence
# 训练模型
sentences = LineSentence('wiki.zh.word.text') #读入语料库
model = Word2Vec(sentences, size=128, window=5, min_count=5, workers=4)
#size:词向量的维数,window上下文考虑的词个数,worker:线程,min_count:词频小于5的不考虑
# 保存模型
model.save('word_embedding_128')
# 加载模型
model = Word2Vec.load("word_embedding_128")
# 使用模型
items = model.most_similar(u'中国')
for item in items:
print item[0],item[1]
model.similarity(u'男人', u'女人')
model.similarity(u'人',u'狗')
'''
'''
官方文档:http://radimrehurek.com/gensim/install.html
'''text2vec包是由Dmitriy Selivanov于2016年10月所写的R包。此包主要是为文本分析和自然语言处理提供了一个简单高效的API框架。由于其由C++所写,同时许多部分(例如GloVe)都充分运用RcppParallel等包进行并行化操作,处理速度得到加速。并且采样流处理器,可以不必把全部数据载入内存才进行分析,有效利用了内存,可以说该包是充分考虑了NLP处理数据量庞大的现实。 text2vec包也可以说是一个文本分析的生态系统,可以进行词向量化操作(Vectorization)、Word2Vec的“升级版GloVe词嵌入表达(与word2vec比较见下图[8])、主题模型分析以及相似性度量四大方面,可以说非常的强大和实用。详情可见官网现在就以官网给出的例子,分别来看看这个生态系统的使用吧!
具体的网上有大量的学习资源
[1] I. Feinerer. An introduction to text mining in R. R News, 8(2):19–22, Oct. 2008. URL http: //CRAN.R-project.org/doc/Rnews/.
[2] I. Feinerer, K. Hornik, and D. Meyer. Text mining infrastructure in R. Journal of Statistical Software, 25(5):1–54, March 2008. ISSN 1548-7660. URL http://www.jstatsoft.org/v25/i05.
[3] H. Ronggui. rmmseg4j: Chinese word segmentation based on mmseg4j, 2011. URL http://RForge.R-project.org/projects/rqda/. R package version 0.1-0/r389.
[4] David M. Blei etal. Latent Dirichlet Allocation. Journal of Machine Learning Research 3 (2003) 993-1022.
[5] Tomas Mikolov et al. Distributed Representations ofWords and Phrases and their Compositionality.
[6] A Fast Clustering Algorithm to Cluster Very Large Categorical Data Sets in Data Mining.(kmodes)
[7] CLUSTERING LARGE DATA SETS WITH MIXED NUMERIC AND CATEGORICAL VALUES.(k-prototypes)
NLP-机器学习的一个分支。目标是使机器能学习,深度学习,识别,理解人类使用的自然语言(语音,字符文字,图像文字等),具备使用自然语言与人类进行交流的能力是目前机器学习领域最困难的技术之一,里面的难点大部分成为各个应用领域(搜索引擎,情感识别,机器写作等等)的核心障碍,是实现高度智能机器人的关键技。这是一条不归路!
关注徐静Github主页,了解如何快速写R packages,及关于R,Python,Ubuntu,Rstudio Server,Shiny Server, Shinyjs,机器学习,深度学习的相关知识。